diff --git a/apps/docs/src/en/guide/advanced/fixed-layout/node.mdx b/apps/docs/src/en/guide/advanced/fixed-layout/node.mdx
index ed402a1b..b1224d40 100644
--- a/apps/docs/src/en/guide/advanced/fixed-layout/node.mdx
+++ b/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)
+```tsx pure
+function BaseNode() {
+ const { id, type, data, updateData, node } = useNodeRender()
+}
+```
+
## Creating Nodes
Create through [FlowOperationService](/api/services/flow-operation-service.html)
@@ -163,23 +169,19 @@ function BaseNode({ node }) {
```tsx pure
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) {
- form.setValueIn('title', e.target.value)
+ updateData({
+ ...data,
+ title: e.target.value
+ })
}
- return
+ return
}
+
```
- Update form data through Field, see [Form Usage](/guide/advanced/form.html) for details
diff --git a/apps/docs/src/en/guide/advanced/free-layout/node.mdx b/apps/docs/src/en/guide/advanced/free-layout/node.mdx
index 8c749652..fcf0c927 100644
--- a/apps/docs/src/en/guide/advanced/free-layout/node.mdx
+++ b/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)
+```tsx pure
+function BaseNode() {
+ const { id, type, data, updateData, node } = useNodeRender()
+}
+```
+
## Create Node
- Through [WorkflowDocument](/api/core/workflow-document.html)
@@ -96,23 +102,19 @@ function BaseNode({ node }) {
```tsx pure
function BaseNode() {
- const { form } = useNodeRender();
+ const { data, updateData } = useNodeRender();
// 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) {
- form.setValueIn('title', e.target.value)
+ updateData({
+ ...data,
+ title: e.target.value
+ })
}
- return
+ return
}
+
```
- Update form data through Field, see details in [Form Usage](/guide/advanced/form.html)
diff --git a/apps/docs/src/zh/guide/advanced/fixed-layout/node.mdx b/apps/docs/src/zh/guide/advanced/fixed-layout/node.mdx
index 5a4f0b37..8efef272 100644
--- a/apps/docs/src/zh/guide/advanced/fixed-layout/node.mdx
+++ b/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) 获取节点相关方法
+```tsx pure
+function BaseNode() {
+ const { id, type, data, updateData, node } = useNodeRender()
+}
+```
+
## 创建节点
通过 [FlowOperationService](/api/services/flow-operation-service.html) 创建
@@ -164,22 +170,17 @@ function BaseNode({ node }) {
```tsx pure
function BaseNode() {
- const { form } = useNodeRender();
+ const { data, updateData } = useNodeRender();
// 对应节点的 data 数据
- console.log(form.values)
-
- // 监听节点的数据变化
- useEffect(() => {
- const toDispose = form.onFormValuesChange(() => {
- // changed
- })
- return () => toDispose.dispose()
- }, [form])
+ console.log(data)
function onChange(e) {
- form.setValueIn('title', e.target.value)
+ updateData({
+ ...data,
+ title: e.target.value
+ })
}
- return
+ return
}
```
- 通过 Field 更新表单数据, 详细见 [表单的使用](/guide/advanced/form.html)
diff --git a/apps/docs/src/zh/guide/advanced/free-layout/node.mdx b/apps/docs/src/zh/guide/advanced/free-layout/node.mdx
index 636c67fc..8f246515 100644
--- a/apps/docs/src/zh/guide/advanced/free-layout/node.mdx
+++ b/apps/docs/src/zh/guide/advanced/free-layout/node.mdx
@@ -37,6 +37,12 @@ const nodeData: FlowNodeJSON = {
通过 [useNodeRender](api/hooks/use-node-render.html) 获取节点相关方法
+```tsx pure
+function BaseNode() {
+ const { id, type, data, updateData, node } = useNodeRender()
+}
+```
+
## 创建节点
- 通过 [WorkflowDocument](/api/core/workflow-document.html) 创建
@@ -96,22 +102,17 @@ function BaseNode({ node }) {
```tsx pure
function BaseNode() {
- const { form } = useNodeRender();
+ const { data, updateData } = useNodeRender();
// 对应节点的 data 数据
- console.log(form.values)
-
- // 监听节点的数据变化
- useEffect(() => {
- const toDispose = form.onFormValuesChange(() => {
- // changed
- })
- return () => toDispose.dispose()
- }, [form])
+ console.log(data)
function onChange(e) {
- form.setValueIn('title', e.target.value)
+ updateData({
+ ...data,
+ title: e.target.value
+ })
}
- return
+ return
}
```
- 通过 Field 更新表单数据, 详细见 [表单的使用](/guide/advanced/form.html)
diff --git a/packages/canvas-engine/free-layout-core/src/hooks/typings.ts b/packages/canvas-engine/free-layout-core/src/hooks/typings.ts
index c8b42f2e..f158f071 100644
--- a/packages/canvas-engine/free-layout-core/src/hooks/typings.ts
+++ b/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';
export interface NodeRenderReturnType {
+ id: string;
+ type: string | number;
/**
* 当前节点
*/
node: FlowNodeEntity;
+ /**
+ * 节点 data 数据
+ */
+ data: any;
+ /**
+ * 更新节点 data 数据
+ */
+ updateData: (newData: any) => void;
/**
* 节点选中
*/
diff --git a/packages/canvas-engine/free-layout-core/src/hooks/use-node-render.tsx b/packages/canvas-engine/free-layout-core/src/hooks/use-node-render.tsx
index b11e9de6..1ca0420a 100644
--- a/packages/canvas-engine/free-layout-core/src/hooks/use-node-render.tsx
+++ b/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')
);
}
+
export function useNodeRender(nodeFromProps?: WorkflowNodeEntity): NodeRenderReturnType {
const node = nodeFromProps || useContext(PlaygroundEntityContext);
const renderData = node.getData(FlowNodeRenderData)!;
@@ -33,6 +34,9 @@ export function useNodeRender(nodeFromProps?: WorkflowNodeEntity): NodeRenderRet
const dragService = useService(WorkflowDragService);
const selectionService = useService(WorkflowSelectService);
const isDragging = useRef(false);
+ const [formValueVersion, updateFormValueVersion] = useState(0);
+ const formValueDependRef = useRef(false);
+ formValueDependRef.current = false;
const nodeRef = useRef(null);
const [linkingNodeId, setLinkingNodeId] = useState('');
@@ -126,35 +130,85 @@ export function useNodeRender(nodeFromProps?: WorkflowNodeEntity): NodeRenderRet
const toggleExpand = useCallback(() => {
renderData.toggleExpand();
}, [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,
+ ]
+ );
}
diff --git a/packages/client/fixed-layout-editor/src/hooks/use-node-render.tsx b/packages/client/fixed-layout-editor/src/hooks/use-node-render.tsx
index 95b000ad..ac749e3c 100644
--- a/packages/client/fixed-layout-editor/src/hooks/use-node-render.tsx
+++ b/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 { useStartDragNode } from '@flowgram.ai/fixed-drag-plugin';
@@ -17,6 +17,16 @@ import {
import { FlowOperationService } from '../types';
export interface NodeRenderReturnType {
+ id: string;
+ type: string | number;
+ /**
+ * 节点 data 数据
+ */
+ data: any;
+ /**
+ * 更新节点 data 数据
+ */
+ updateData: (newData: any) => void;
/**
* BlockOrderIcon节点,一般用于分支的第一个占位节点
*/
@@ -100,6 +110,9 @@ export function useNodeRender(nodeFromProps?: FlowNodeEntity): NodeRenderReturnT
const playground = usePlayground();
const isBlockOrderIcon = renderNode.flowNodeType === FlowNodeBaseType.BLOCK_ORDER_ICON;
const isBlockIcon = renderNode.flowNodeType === FlowNodeBaseType.BLOCK_ICON;
+ const [formValueVersion, updateFormValueVersion] = useState(0);
+ const formValueDependRef = useRef(false);
+ formValueDependRef.current = false;
// 在 BlockIcon 情况,如果在触发 fromJSON 时候更新表单数据导致刷新节点会存在 renderNode.parent 为 undefined,所以这里 nodeCache 进行缓存
const node =
(isBlockOrderIcon || isBlockIcon ? renderNode.parent! : renderNode) || nodeCache.current;
@@ -154,10 +167,35 @@ export function useNodeRender(nodeFromProps?: FlowNodeEntity): NodeRenderReturnT
return () => dispose?.dispose();
}, [renderNode, isBlockIcon, isBlockOrderIcon]);
+ useEffect(() => {
+ const toDispose = form?.onFormValuesChange(() => {
+ if (formValueDependRef.current) {
+ updateFormValueVersion((v) => v + 1);
+ }
+ });
+ return () => toDispose?.dispose();
+ }, [form]);
+
const readonly = playground.config.readonly;
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,
isBlockOrderIcon,
isBlockIcon,
@@ -177,6 +215,7 @@ export function useNodeRender(nodeFromProps?: FlowNodeEntity): NodeRenderReturnT
return {
...form,
get values() {
+ formValueDependRef.current = true;
return form.values!;
},
get state() {
@@ -202,6 +241,7 @@ export function useNodeRender(nodeFromProps?: FlowNodeEntity): NodeRenderReturnT
toggleExpand,
form,
formState,
+ formValueVersion,
]
);
}