Browse Source

feat: use-node-render add id,type,data,updateData (#384)

xiamidaxia 7 months ago
parent
commit
3dda7cfdf3

+ 15 - 13
apps/docs/src/en/guide/advanced/fixed-layout/node.mdx

@@ -96,6 +96,12 @@ export const nodeRegistries: FlowNodeRegistry[] = [
 
 
 Get node-related methods through [useNodeRender](/api/hooks/use-node-render.html)
 Get node-related methods through [useNodeRender](/api/hooks/use-node-render.html)
 
 
+```tsx pure
+function BaseNode() {
+  const { id, type, data, updateData, node } = useNodeRender()
+}
+```
+
 ## Creating Nodes
 ## Creating Nodes
 
 
 Create through [FlowOperationService](/api/services/flow-operation-service.html)
 Create through [FlowOperationService](/api/services/flow-operation-service.html)
@@ -163,23 +169,19 @@ function BaseNode({ node }) {
 
 
 ```tsx pure
 ```tsx pure
 function BaseNode() {
 function BaseNode() {
-  const { form } = useNodeRender();
-  // Corresponding node data
-  console.log(form.values)
-
-  // Monitor node data changes
-  useEffect(() => {
-    const toDispose = form.onFormValuesChange(() => {
-      // changed
-    })
-    return () => toDispose.dispose()
-  }, [form])
+  const { data, updateData } = useNodeRender();
+  // Corresponds to node's data
+  console.log(data)
 
 
   function onChange(e) {
   function onChange(e) {
-    form.setValueIn('title', e.target.value)
+    updateData({
+      ...data,
+      title: e.target.value
+    })
   }
   }
-  return <input value={form.getValueIn('title')} onChange={onChange}/>
+  return <input value={data.title} onChange={onChange}/>
 }
 }
+
 ```
 ```
 
 
 - Update form data through Field, see [Form Usage](/guide/advanced/form.html) for details
 - Update form data through Field, see [Form Usage](/guide/advanced/form.html) for details

+ 14 - 12
apps/docs/src/en/guide/advanced/free-layout/node.mdx

@@ -36,6 +36,12 @@ In free layout scenarios, node definition is used to declare node's initial posi
 
 
 Get node-related methods through [useNodeRender](api/hooks/use-node-render.html)
 Get node-related methods through [useNodeRender](api/hooks/use-node-render.html)
 
 
+```tsx pure
+function BaseNode() {
+  const { id, type, data, updateData, node } = useNodeRender()
+}
+```
+
 ## Create Node
 ## Create Node
 
 
 - Through [WorkflowDocument](/api/core/workflow-document.html)
 - Through [WorkflowDocument](/api/core/workflow-document.html)
@@ -96,23 +102,19 @@ function BaseNode({ node }) {
 
 
 ```tsx pure
 ```tsx pure
 function BaseNode() {
 function BaseNode() {
-  const { form } = useNodeRender();
+  const { data, updateData } = useNodeRender();
   // Corresponds to node's data
   // Corresponds to node's data
-  console.log(form.values)
-
-  // Listen to node data changes
-  useEffect(() => {
-    const toDispose = form.onFormValuesChange(() => {
-      // changed
-    })
-    return () => toDispose.dispose()
-  }, [form])
+  console.log(data)
 
 
   function onChange(e) {
   function onChange(e) {
-    form.setValueIn('title', e.target.value)
+    updateData({
+      ...data,
+      title: e.target.value
+    })
   }
   }
-  return <input value={form.getValueIn('title')} onChange={onChange}/>
+  return <input value={data.title} onChange={onChange}/>
 }
 }
+
 ```
 ```
 - Update form data through Field, see details in [Form Usage](/guide/advanced/form.html)
 - Update form data through Field, see details in [Form Usage](/guide/advanced/form.html)
 
 

+ 13 - 12
apps/docs/src/zh/guide/advanced/fixed-layout/node.mdx

@@ -97,6 +97,12 @@ export const nodeRegistries: FlowNodeRegistry[] = [
 
 
 通过 [useNodeRender](/api/hooks/use-node-render.html) 获取节点相关方法
 通过 [useNodeRender](/api/hooks/use-node-render.html) 获取节点相关方法
 
 
+```tsx pure
+function BaseNode() {
+  const { id, type, data, updateData, node } = useNodeRender()
+}
+```
+
 ## 创建节点
 ## 创建节点
 
 
 通过 [FlowOperationService](/api/services/flow-operation-service.html) 创建
 通过 [FlowOperationService](/api/services/flow-operation-service.html) 创建
@@ -164,22 +170,17 @@ function BaseNode({ node }) {
 
 
 ```tsx pure
 ```tsx pure
 function BaseNode() {
 function BaseNode() {
-  const { form } = useNodeRender();
+  const { data, updateData } = useNodeRender();
   // 对应节点的 data 数据
   // 对应节点的 data 数据
-  console.log(form.values)
-
-  // 监听节点的数据变化
-  useEffect(() => {
-    const toDispose = form.onFormValuesChange(() => {
-      // changed
-    })
-    return () => toDispose.dispose()
-  }, [form])
+  console.log(data)
 
 
   function onChange(e) {
   function onChange(e) {
-    form.setValueIn('title', e.target.value)
+    updateData({
+      ...data,
+      title: e.target.value
+    })
   }
   }
-  return <input value={form.getValueIn('title')} onChange={onChange}/>
+  return <input value={data.title} onChange={onChange}/>
 }
 }
 ```
 ```
 - 通过 Field 更新表单数据, 详细见 [表单的使用](/guide/advanced/form.html)
 - 通过 Field 更新表单数据, 详细见 [表单的使用](/guide/advanced/form.html)

+ 13 - 12
apps/docs/src/zh/guide/advanced/free-layout/node.mdx

@@ -37,6 +37,12 @@ const nodeData: FlowNodeJSON = {
 
 
 通过 [useNodeRender](api/hooks/use-node-render.html) 获取节点相关方法
 通过 [useNodeRender](api/hooks/use-node-render.html) 获取节点相关方法
 
 
+```tsx pure
+function BaseNode() {
+  const { id, type, data, updateData, node } = useNodeRender()
+}
+```
+
 ## 创建节点
 ## 创建节点
 
 
 - 通过 [WorkflowDocument](/api/core/workflow-document.html) 创建
 - 通过 [WorkflowDocument](/api/core/workflow-document.html) 创建
@@ -96,22 +102,17 @@ function BaseNode({ node }) {
 
 
 ```tsx pure
 ```tsx pure
 function BaseNode() {
 function BaseNode() {
-  const { form } = useNodeRender();
+  const { data, updateData } = useNodeRender();
   // 对应节点的 data 数据
   // 对应节点的 data 数据
-  console.log(form.values)
-
-  // 监听节点的数据变化
-  useEffect(() => {
-    const toDispose = form.onFormValuesChange(() => {
-      // changed
-    })
-    return () => toDispose.dispose()
-  }, [form])
+  console.log(data)
 
 
   function onChange(e) {
   function onChange(e) {
-    form.setValueIn('title', e.target.value)
+    updateData({
+      ...data,
+      title: e.target.value
+    })
   }
   }
-  return <input value={form.getValueIn('title')} onChange={onChange}/>
+  return <input value={data.title} onChange={onChange}/>
 }
 }
 ```
 ```
 - 通过 Field 更新表单数据, 详细见 [表单的使用](/guide/advanced/form.html)
 - 通过 Field 更新表单数据, 详细见 [表单的使用](/guide/advanced/form.html)

+ 10 - 0
packages/canvas-engine/free-layout-core/src/hooks/typings.ts

@@ -4,10 +4,20 @@ import { FlowNodeEntity } from '@flowgram.ai/document';
 import { type WorkflowPortEntity } from '../entities';
 import { type WorkflowPortEntity } from '../entities';
 
 
 export interface NodeRenderReturnType {
 export interface NodeRenderReturnType {
+  id: string;
+  type: string | number;
   /**
   /**
    * 当前节点
    * 当前节点
    */
    */
   node: FlowNodeEntity;
   node: FlowNodeEntity;
+  /**
+   * 节点 data 数据
+   */
+  data: any;
+  /**
+   * 更新节点 data 数据
+   */
+  updateData: (newData: any) => void;
   /**
   /**
    * 节点选中
    * 节点选中
    */
    */

+ 84 - 30
packages/canvas-engine/free-layout-core/src/hooks/use-node-render.tsx

@@ -25,6 +25,7 @@ function checkTargetDraggable(el: any): boolean {
     !el.closest('.flow-canvas-not-draggable')
     !el.closest('.flow-canvas-not-draggable')
   );
   );
 }
 }
+
 export function useNodeRender(nodeFromProps?: WorkflowNodeEntity): NodeRenderReturnType {
 export function useNodeRender(nodeFromProps?: WorkflowNodeEntity): NodeRenderReturnType {
   const node = nodeFromProps || useContext<WorkflowNodeEntity>(PlaygroundEntityContext);
   const node = nodeFromProps || useContext<WorkflowNodeEntity>(PlaygroundEntityContext);
   const renderData = node.getData(FlowNodeRenderData)!;
   const renderData = node.getData(FlowNodeRenderData)!;
@@ -33,6 +34,9 @@ export function useNodeRender(nodeFromProps?: WorkflowNodeEntity): NodeRenderRet
   const dragService = useService<WorkflowDragService>(WorkflowDragService);
   const dragService = useService<WorkflowDragService>(WorkflowDragService);
   const selectionService = useService<WorkflowSelectService>(WorkflowSelectService);
   const selectionService = useService<WorkflowSelectService>(WorkflowSelectService);
   const isDragging = useRef(false);
   const isDragging = useRef(false);
+  const [formValueVersion, updateFormValueVersion] = useState<number>(0);
+  const formValueDependRef = useRef(false);
+  formValueDependRef.current = false;
   const nodeRef = useRef<HTMLDivElement | null>(null);
   const nodeRef = useRef<HTMLDivElement | null>(null);
   const [linkingNodeId, setLinkingNodeId] = useState('');
   const [linkingNodeId, setLinkingNodeId] = useState('');
 
 
@@ -126,35 +130,85 @@ export function useNodeRender(nodeFromProps?: WorkflowNodeEntity): NodeRenderRet
   const toggleExpand = useCallback(() => {
   const toggleExpand = useCallback(() => {
     renderData.toggleExpand();
     renderData.toggleExpand();
   }, [renderData]);
   }, [renderData]);
+  const selected = selectionService.isSelected(node.id);
+  const activated = selectionService.isActivated(node.id);
+  const expanded = renderData.expanded;
+  useEffect(() => {
+    const toDispose = form?.onFormValuesChange(() => {
+      if (formValueDependRef.current) {
+        updateFormValueVersion((v) => v + 1);
+      }
+    });
+    return () => toDispose?.dispose();
+  }, [form]);
 
 
-  return {
-    node,
-    selected: selectionService.isSelected(node.id),
-    activated: selectionService.isActivated(node.id),
-    expanded: renderData.expanded,
-    startDrag,
-    ports: portsData.allPorts,
-    deleteNode,
-    selectNode,
-    readonly,
-    linkingNodeId,
-    nodeRef,
-    onFocus,
-    onBlur,
-    getExtInfo,
-    updateExtInfo,
-    toggleExpand,
-    get form() {
-      if (!form) return undefined;
-      return {
-        ...form,
-        get values() {
-          return form.values!;
-        },
-        get state() {
-          return formState;
-        },
-      };
-    },
-  };
+  return useMemo(
+    () => ({
+      id: node.id,
+      type: node.flowNodeType,
+      get data() {
+        if (form) {
+          formValueDependRef.current = true;
+          return form.values;
+        }
+        return getExtInfo();
+      },
+      updateData(values: any) {
+        if (form) {
+          form.updateFormValues(values);
+        } else {
+          updateExtInfo(values);
+        }
+      },
+      node,
+      selected,
+      activated,
+      expanded,
+      startDrag,
+      get ports() {
+        return portsData.allPorts;
+      },
+      deleteNode,
+      selectNode,
+      readonly,
+      linkingNodeId,
+      nodeRef,
+      onFocus,
+      onBlur,
+      getExtInfo,
+      updateExtInfo,
+      toggleExpand,
+      get form() {
+        if (!form) return undefined;
+        return {
+          ...form,
+          get values() {
+            formValueDependRef.current = true;
+            return form.values!;
+          },
+          get state() {
+            return formState;
+          },
+        };
+      },
+    }),
+    [
+      node,
+      selected,
+      activated,
+      expanded,
+      startDrag,
+      deleteNode,
+      selectNode,
+      readonly,
+      linkingNodeId,
+      nodeRef,
+      onFocus,
+      onBlur,
+      getExtInfo,
+      updateExtInfo,
+      toggleExpand,
+      formValueVersion,
+    ]
+  );
 }
 }

+ 41 - 1
packages/client/fixed-layout-editor/src/hooks/use-node-render.tsx

@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect, useContext, useMemo, useRef } from 'react';
+import React, { useCallback, useEffect, useContext, useMemo, useRef, useState } from 'react';
 
 
 import { useObserve } from '@flowgram.ai/reactive';
 import { useObserve } from '@flowgram.ai/reactive';
 import { useStartDragNode } from '@flowgram.ai/fixed-drag-plugin';
 import { useStartDragNode } from '@flowgram.ai/fixed-drag-plugin';
@@ -17,6 +17,16 @@ import {
 import { FlowOperationService } from '../types';
 import { FlowOperationService } from '../types';
 
 
 export interface NodeRenderReturnType {
 export interface NodeRenderReturnType {
+  id: string;
+  type: string | number;
+  /**
+   * 节点 data 数据
+   */
+  data: any;
+  /**
+   * 更新节点 data 数据
+   */
+  updateData: (newData: any) => void;
   /**
   /**
    * BlockOrderIcon节点,一般用于分支的第一个占位节点
    * BlockOrderIcon节点,一般用于分支的第一个占位节点
    */
    */
@@ -100,6 +110,9 @@ export function useNodeRender(nodeFromProps?: FlowNodeEntity): NodeRenderReturnT
   const playground = usePlayground();
   const playground = usePlayground();
   const isBlockOrderIcon = renderNode.flowNodeType === FlowNodeBaseType.BLOCK_ORDER_ICON;
   const isBlockOrderIcon = renderNode.flowNodeType === FlowNodeBaseType.BLOCK_ORDER_ICON;
   const isBlockIcon = renderNode.flowNodeType === FlowNodeBaseType.BLOCK_ICON;
   const isBlockIcon = renderNode.flowNodeType === FlowNodeBaseType.BLOCK_ICON;
+  const [formValueVersion, updateFormValueVersion] = useState<number>(0);
+  const formValueDependRef = useRef(false);
+  formValueDependRef.current = false;
   // 在 BlockIcon 情况,如果在触发 fromJSON 时候更新表单数据导致刷新节点会存在 renderNode.parent 为 undefined,所以这里 nodeCache 进行缓存
   // 在 BlockIcon 情况,如果在触发 fromJSON 时候更新表单数据导致刷新节点会存在 renderNode.parent 为 undefined,所以这里 nodeCache 进行缓存
   const node =
   const node =
     (isBlockOrderIcon || isBlockIcon ? renderNode.parent! : renderNode) || nodeCache.current;
     (isBlockOrderIcon || isBlockIcon ? renderNode.parent! : renderNode) || nodeCache.current;
@@ -154,10 +167,35 @@ export function useNodeRender(nodeFromProps?: FlowNodeEntity): NodeRenderReturnT
     return () => dispose?.dispose();
     return () => dispose?.dispose();
   }, [renderNode, isBlockIcon, isBlockOrderIcon]);
   }, [renderNode, isBlockIcon, isBlockOrderIcon]);
 
 
+  useEffect(() => {
+    const toDispose = form?.onFormValuesChange(() => {
+      if (formValueDependRef.current) {
+        updateFormValueVersion((v) => v + 1);
+      }
+    });
+    return () => toDispose?.dispose();
+  }, [form]);
+
   const readonly = playground.config.readonly;
   const readonly = playground.config.readonly;
 
 
   return useMemo(
   return useMemo(
     () => ({
     () => ({
+      id: node.id,
+      type: node.flowNodeType,
+      get data() {
+        if (form) {
+          formValueDependRef.current = true;
+          return form.values;
+        }
+        return getExtInfo();
+      },
+      updateData(values: any) {
+        if (form) {
+          form.updateFormValues(values);
+        } else {
+          updateExtInfo(values);
+        }
+      },
       node,
       node,
       isBlockOrderIcon,
       isBlockOrderIcon,
       isBlockIcon,
       isBlockIcon,
@@ -177,6 +215,7 @@ export function useNodeRender(nodeFromProps?: FlowNodeEntity): NodeRenderReturnT
         return {
         return {
           ...form,
           ...form,
           get values() {
           get values() {
+            formValueDependRef.current = true;
             return form.values!;
             return form.values!;
           },
           },
           get state() {
           get state() {
@@ -202,6 +241,7 @@ export function useNodeRender(nodeFromProps?: FlowNodeEntity): NodeRenderReturnT
       toggleExpand,
       toggleExpand,
       form,
       form,
       formState,
       formState,
+      formValueVersion,
     ]
     ]
   );
   );
 }
 }