### What problem does this PR solve? Feat: Use memo to wrap canvas nodes to improve fluency #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.19.1
| @@ -1,4 +1,5 @@ | |||
| import { getLLMIconName, getLlmNameAndFIdByLlmId } from '@/utils/llm-util'; | |||
| import { memo } from 'react'; | |||
| import { LlmIcon } from '../svg-icon'; | |||
| interface IProps { | |||
| @@ -24,4 +25,4 @@ const LLMLabel = ({ value }: IProps) => { | |||
| ); | |||
| }; | |||
| export default LLMLabel; | |||
| export default memo(LLMLabel); | |||
| @@ -1,7 +1,7 @@ | |||
| import { LlmModelType } from '@/constants/knowledge'; | |||
| import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks'; | |||
| import * as SelectPrimitive from '@radix-ui/react-select'; | |||
| import { forwardRef, useState } from 'react'; | |||
| import { forwardRef, memo, useState } from 'react'; | |||
| import { LlmSettingFieldItems } from '../llm-setting-items/next'; | |||
| import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'; | |||
| import { Select, SelectTrigger, SelectValue } from '../ui/select'; | |||
| @@ -14,7 +14,7 @@ interface IProps { | |||
| disabled?: boolean; | |||
| } | |||
| export const NextLLMSelect = forwardRef< | |||
| const NextInnerLLMSelect = forwardRef< | |||
| React.ElementRef<typeof SelectPrimitive.Trigger>, | |||
| IProps | |||
| >(({ value, disabled }, ref) => { | |||
| @@ -52,4 +52,6 @@ export const NextLLMSelect = forwardRef< | |||
| ); | |||
| }); | |||
| NextLLMSelect.displayName = 'LLMSelect'; | |||
| NextInnerLLMSelect.displayName = 'LLMSelect'; | |||
| export const NextLLMSelect = memo(NextInnerLLMSelect); | |||
| @@ -14,7 +14,7 @@ import { | |||
| SidebarHeader, | |||
| SidebarMenu, | |||
| } from '@/components/ui/sidebar'; | |||
| import { useMemo } from 'react'; | |||
| import { memo, useMemo } from 'react'; | |||
| import { | |||
| AgentOperatorList, | |||
| Operator, | |||
| @@ -77,7 +77,7 @@ function OperatorCollapsible({ | |||
| ); | |||
| } | |||
| export function AgentSidebar() { | |||
| function InnerAgentSidebar() { | |||
| const agentOperatorList = useMemo(() => { | |||
| return componentMenuList.filter((x) => | |||
| AgentOperatorList.some((y) => y === x.name), | |||
| @@ -108,3 +108,5 @@ export function AgentSidebar() { | |||
| </Sidebar> | |||
| ); | |||
| } | |||
| export const AgentSidebar = memo(InnerAgentSidebar); | |||
| @@ -18,7 +18,7 @@ import { RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| // TODO: do not allow other nodes to connect to this node | |||
| function Node({ selected, data }: NodeProps<IBeginNode>) { | |||
| function InnerBeginNode({ selected, data }: NodeProps<IBeginNode>) { | |||
| const { t } = useTranslation(); | |||
| const query: BeginQuery[] = get(data, 'form.query', []); | |||
| const { theme } = useTheme(); | |||
| @@ -72,4 +72,4 @@ function Node({ selected, data }: NodeProps<IBeginNode>) { | |||
| ); | |||
| } | |||
| export const BeginNode = memo(Node); | |||
| export const BeginNode = memo(InnerBeginNode); | |||
| @@ -1,6 +1,6 @@ | |||
| import { Handle, Position } from '@xyflow/react'; | |||
| import React from 'react'; | |||
| import React, { memo } from 'react'; | |||
| import styles from './index.less'; | |||
| const DEFAULT_HANDLE_STYLE = { | |||
| @@ -37,4 +37,4 @@ const CategorizeHandle = ({ top, right, id, children }: IProps) => { | |||
| ); | |||
| }; | |||
| export default CategorizeHandle; | |||
| export default memo(CategorizeHandle); | |||
| @@ -5,12 +5,13 @@ import { Handle, NodeProps, Position } from '@xyflow/react'; | |||
| import { Flex } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { get } from 'lodash'; | |||
| import { memo } from 'react'; | |||
| import { RightHandleStyle } from './handle-icon'; | |||
| import { useBuildCategorizeHandlePositions } from './hooks'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| export function CategorizeNode({ | |||
| export function InnerCategorizeNode({ | |||
| id, | |||
| data, | |||
| selected, | |||
| @@ -66,3 +67,5 @@ export function CategorizeNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const CategorizeNode = memo(InnerCategorizeNode); | |||
| @@ -2,12 +2,12 @@ import { IEmailNode } from '@/interfaces/database/flow'; | |||
| import { Handle, NodeProps, Position } from '@xyflow/react'; | |||
| import { Flex } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { useState } from 'react'; | |||
| import { memo, useState } from 'react'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| export function EmailNode({ | |||
| export function InnerEmailNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| @@ -76,3 +76,5 @@ export function EmailNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const EmailNode = memo(InnerEmailNode); | |||
| @@ -4,11 +4,12 @@ import { IGenerateNode } from '@/interfaces/database/flow'; | |||
| import { Handle, NodeProps, Position } from '@xyflow/react'; | |||
| import classNames from 'classnames'; | |||
| import { get } from 'lodash'; | |||
| import { memo } from 'react'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| export function GenerateNode({ | |||
| export function InnerGenerateNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| @@ -55,3 +56,5 @@ export function GenerateNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const GenerateNode = memo(InnerGenerateNode); | |||
| @@ -2,11 +2,12 @@ import { useTheme } from '@/components/theme-provider'; | |||
| import { IRagNode } from '@/interfaces/database/flow'; | |||
| import { Handle, NodeProps, Position } from '@xyflow/react'; | |||
| import classNames from 'classnames'; | |||
| import { memo } from 'react'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| export function RagNode({ | |||
| function InnerRagNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| @@ -43,3 +44,5 @@ export function RagNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const RagNode = memo(InnerRagNode); | |||
| @@ -4,12 +4,13 @@ import { Handle, NodeProps, Position } from '@xyflow/react'; | |||
| import { Flex } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { get } from 'lodash'; | |||
| import { memo } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| export function InvokeNode({ | |||
| function InnerInvokeNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| @@ -57,3 +58,5 @@ export function InvokeNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const InvokeNode = memo(InnerInvokeNode); | |||
| @@ -6,6 +6,7 @@ import { | |||
| import { cn } from '@/lib/utils'; | |||
| import { Handle, NodeProps, NodeResizeControl, Position } from '@xyflow/react'; | |||
| import { ListRestart } from 'lucide-react'; | |||
| import { memo } from 'react'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| @@ -43,7 +44,7 @@ const controlStyle = { | |||
| cursor: 'nwse-resize', | |||
| }; | |||
| export function IterationNode({ | |||
| export function InnerIterationNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| @@ -98,7 +99,7 @@ export function IterationNode({ | |||
| ); | |||
| } | |||
| export function IterationStartNode({ | |||
| function InnerIterationStartNode({ | |||
| isConnectable = true, | |||
| selected, | |||
| }: NodeProps<IIterationStartNode>) { | |||
| @@ -125,3 +126,7 @@ export function IterationStartNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const IterationStartNode = memo(InnerIterationStartNode); | |||
| export const IterationNode = memo(InnerIterationNode); | |||
| @@ -4,11 +4,12 @@ import { IKeywordNode } from '@/interfaces/database/flow'; | |||
| import { Handle, NodeProps, Position } from '@xyflow/react'; | |||
| import classNames from 'classnames'; | |||
| import { get } from 'lodash'; | |||
| import { memo } from 'react'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| export function KeywordNode({ | |||
| export function InnerKeywordNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| @@ -55,3 +56,5 @@ export function KeywordNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const KeywordNode = memo(InnerKeywordNode); | |||
| @@ -2,11 +2,12 @@ import { useTheme } from '@/components/theme-provider'; | |||
| import { ILogicNode } from '@/interfaces/database/flow'; | |||
| import { Handle, NodeProps, Position } from '@xyflow/react'; | |||
| import classNames from 'classnames'; | |||
| import { memo } from 'react'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| export function LogicNode({ | |||
| export function InnerLogicNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| @@ -43,3 +44,5 @@ export function LogicNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const LogicNode = memo(InnerLogicNode); | |||
| @@ -4,11 +4,12 @@ import { Handle, NodeProps, Position } from '@xyflow/react'; | |||
| import { Flex } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { get } from 'lodash'; | |||
| import { memo } from 'react'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| export function MessageNode({ | |||
| function InnerMessageNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| @@ -63,3 +64,5 @@ export function MessageNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const MessageNode = memo(InnerMessageNode); | |||
| @@ -7,6 +7,7 @@ import { needsSingleStepDebugging } from '../../utils'; | |||
| import NodeDropdown from './dropdown'; | |||
| import { NextNodePopover } from './popover'; | |||
| import { memo } from 'react'; | |||
| import { RunTooltip } from '../../flow-tooltip'; | |||
| interface IProps { | |||
| id: string; | |||
| @@ -37,7 +38,7 @@ export function RunStatus({ id, name, label }: IProps) { | |||
| ); | |||
| } | |||
| const NodeHeader = ({ | |||
| const InnerNodeHeader = ({ | |||
| label, | |||
| id, | |||
| name, | |||
| @@ -70,4 +71,6 @@ const NodeHeader = ({ | |||
| ); | |||
| }; | |||
| const NodeHeader = memo(InnerNodeHeader); | |||
| export default NodeHeader; | |||
| @@ -6,11 +6,12 @@ import { RightHandleStyle } from './handle-icon'; | |||
| import { useTheme } from '@/components/theme-provider'; | |||
| import { IRelevantNode } from '@/interfaces/database/flow'; | |||
| import { get } from 'lodash'; | |||
| import { memo } from 'react'; | |||
| import { useReplaceIdWithName } from '../../hooks'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| export function RelevantNode({ id, data, selected }: NodeProps<IRelevantNode>) { | |||
| function InnerRelevantNode({ id, data, selected }: NodeProps<IRelevantNode>) { | |||
| const yes = get(data, 'form.yes'); | |||
| const no = get(data, 'form.no'); | |||
| const replaceIdWithName = useReplaceIdWithName(); | |||
| @@ -68,3 +69,5 @@ export function RelevantNode({ id, data, selected }: NodeProps<IRelevantNode>) { | |||
| </section> | |||
| ); | |||
| } | |||
| export const RelevantNode = memo(InnerRelevantNode); | |||
| @@ -6,12 +6,12 @@ import { Handle, NodeProps, Position } from '@xyflow/react'; | |||
| import { Avatar, Flex } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { get } from 'lodash'; | |||
| import { useMemo } from 'react'; | |||
| import { memo, useMemo } from 'react'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| export function RetrievalNode({ | |||
| function InnerRetrievalNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| @@ -86,3 +86,5 @@ export function RetrievalNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const RetrievalNode = memo(InnerRetrievalNode); | |||
| @@ -4,11 +4,12 @@ import { IRewriteNode } from '@/interfaces/database/flow'; | |||
| import { Handle, NodeProps, Position } from '@xyflow/react'; | |||
| import classNames from 'classnames'; | |||
| import { get } from 'lodash'; | |||
| import { memo } from 'react'; | |||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| export function RewriteNode({ | |||
| function InnerRewriteNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| @@ -55,3 +56,5 @@ export function RewriteNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const RewriteNode = memo(InnerRewriteNode); | |||
| @@ -3,6 +3,7 @@ import { ISwitchCondition, ISwitchNode } from '@/interfaces/database/flow'; | |||
| import { Handle, NodeProps, Position } from '@xyflow/react'; | |||
| import { Divider, Flex } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import { memo } from 'react'; | |||
| import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query'; | |||
| import { RightHandleStyle } from './handle-icon'; | |||
| import { useBuildSwitchHandlePositions } from './hooks'; | |||
| @@ -54,7 +55,7 @@ const ConditionBlock = ({ | |||
| ); | |||
| }; | |||
| export function SwitchNode({ id, data, selected }: NodeProps<ISwitchNode>) { | |||
| function InnerSwitchNode({ id, data, selected }: NodeProps<ISwitchNode>) { | |||
| const { positions } = useBuildSwitchHandlePositions({ data, id }); | |||
| const { theme } = useTheme(); | |||
| return ( | |||
| @@ -112,3 +113,5 @@ export function SwitchNode({ id, data, selected }: NodeProps<ISwitchNode>) { | |||
| </section> | |||
| ); | |||
| } | |||
| export const SwitchNode = memo(InnerSwitchNode); | |||
| @@ -9,9 +9,10 @@ import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | |||
| import NodeHeader from './node-header'; | |||
| import { ITemplateNode } from '@/interfaces/database/flow'; | |||
| import { memo } from 'react'; | |||
| import styles from './index.less'; | |||
| export function TemplateNode({ | |||
| function InnerTemplateNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| @@ -73,3 +74,5 @@ export function TemplateNode({ | |||
| </section> | |||
| ); | |||
| } | |||
| export const TemplateNode = memo(InnerTemplateNode); | |||