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

This commit is contained in:
xiamidaxia 2025-06-17 17:53:17 +08:00 committed by GitHub
parent 77d8a893cb
commit 3dda7cfdf3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 190 additions and 80 deletions

View File

@ -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(); const { data, updateData } = useNodeRender();
// Corresponding node data // Corresponds to node's data
console.log(form.values) console.log(data)
// Monitor node data changes
useEffect(() => {
const toDispose = form.onFormValuesChange(() => {
// changed
})
return () => toDispose.dispose()
}, [form])
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

View File

@ -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) console.log(data)
// Listen to node data changes
useEffect(() => {
const toDispose = form.onFormValuesChange(() => {
// changed
})
return () => toDispose.dispose()
}, [form])
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)

View File

@ -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) console.log(data)
// 监听节点的数据变化
useEffect(() => {
const toDispose = form.onFormValuesChange(() => {
// changed
})
return () => toDispose.dispose()
}, [form])
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)

View File

@ -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) console.log(data)
// 监听节点的数据变化
useEffect(() => {
const toDispose = form.onFormValuesChange(() => {
// changed
})
return () => toDispose.dispose()
}, [form])
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)

View File

@ -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;
/** /**
* *
*/ */

View File

@ -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,14 +130,44 @@ 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 { 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,
selected: selectionService.isSelected(node.id), selected,
activated: selectionService.isActivated(node.id), activated,
expanded: renderData.expanded, expanded,
startDrag, startDrag,
ports: portsData.allPorts, get ports() {
return portsData.allPorts;
},
deleteNode, deleteNode,
selectNode, selectNode,
readonly, readonly,
@ -149,6 +183,7 @@ export function useNodeRender(nodeFromProps?: WorkflowNodeEntity): NodeRenderRet
return { return {
...form, ...form,
get values() { get values() {
formValueDependRef.current = true;
return form.values!; return form.values!;
}, },
get state() { get state() {
@ -156,5 +191,24 @@ export function useNodeRender(nodeFromProps?: WorkflowNodeEntity): NodeRenderRet
}, },
}; };
}, },
}; }),
[
node,
selected,
activated,
expanded,
startDrag,
deleteNode,
selectNode,
readonly,
linkingNodeId,
nodeRef,
onFocus,
onBlur,
getExtInfo,
updateExtInfo,
toggleExpand,
formValueVersion,
]
);
} }

View File

@ -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,
] ]
); );
} }