Skip to content

Commit 8b34f6d

Browse files
committed
chore: 优化 FlowProvider 组件和文档
1 parent eb5a0f8 commit 8b34f6d

File tree

12 files changed

+126
-95
lines changed

12 files changed

+126
-95
lines changed

docs/xflow/FlowProvider.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,11 @@ group:
99

1010
# 基础交互
1111

12+
`<FlowProvider/>` 组件是一个 Context Provider,使得在 `<XFlow/>` 组件之外访问流的内部状态成为可能。我们提供的 `useFlow()``useNodes()` 钩子依赖于这个组件才能工作。
13+
1214
<code src="./demo/flow-provider/index.tsx"></code>
15+
16+
## 注意
17+
18+
- 如果你正在使用路由器并且希望流程的状态在不同路由之间保持持久,那么将 `<FlowProvider/>` 组件放置在路由器外部是至关重要的。
19+
- 如果在同一页面上有多个 `<XFlow/>`,则需要为每个 `<XFlow/>` 使用单独的 `<FlowProvider/>`

docs/xflow/demo/flow-provider/index.tsx

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import * as React from "react";
22

33
import XFlow, { FlowProvider, useNodes } from '@xrenders/xflow';
44
import { edges as initialEdges } from './edges';
@@ -7,18 +7,18 @@ import settings from './setting';
77

88
const App = () => {
99
return (
10-
<FlowProvider>
11-
<div style={{ height: '600px' }}>
12-
<XFlow
13-
initialValues={{ nodes: initialNodes, edges: initialEdges }}
14-
settings={settings}
15-
nodeSelector={{
16-
showSearch: true,
17-
}}
18-
/>
19-
</div>
20-
<Sidebar />
21-
</FlowProvider>
10+
<FlowProvider>
11+
<div style={{ height: '600px' }}>
12+
<XFlow
13+
initialValues={{ nodes: initialNodes, edges: initialEdges }}
14+
settings={settings as any[]}
15+
nodeSelector={{
16+
showSearch: true,
17+
}}
18+
/>
19+
</div>
20+
<Sidebar />
21+
</FlowProvider>
2222
);
2323
};
2424

@@ -30,9 +30,9 @@ function Sidebar() {
3030
return (
3131
<aside>
3232
{nodes?.map(node => (
33-
<div key={node.id}>
34-
Node {node.id} - x: {node.position.x.toFixed(2)}, y:{' '}
35-
{node.position.y.toFixed(2)}
33+
<div key={node?.id}>
34+
Node {node?.id} - x: {node?.position?.x?.toFixed(2)}, y:{' '}
35+
{node?.position?.y?.toFixed(2)}
3636
</div>
3737
))}
3838
</aside>

docs/xflow/demo/layout/TB/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ export default () => {
6262
<div style={{ height: '600px' }}>
6363
<XFlow
6464
initialValues={{ nodes, edges }}
65-
settings={settings}
65+
settings={settings as any}
6666
nodeSelector={{
6767
showSearch: true,
6868
}}
69-
layout="TB"
69+
layout="LR"
7070
/>
7171
</div>
7272
);

packages/x-flow/src/XFlow.tsx

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,16 @@ import { useEventEmitterContextContext } from './models/event-emitter';
1818

1919
import CustomNodeComponent from './components/CustomNode';
2020
import { useStore, useStoreApi } from './hooks/useStore';
21-
import { useTemporalStore } from './hooks/useTemporalStore';
2221

2322
import Operator from './operator';
2423
import FlowProps from './types';
25-
import { transformNodes, uuid } from './utils';
24+
import { uuid } from './utils';
2625
import autoLayoutNodes from './utils/autoLayoutNodes';
2726

2827
import { shallow } from 'zustand/shallow';
2928
import NodeEditor from './components/NodeEditor';
30-
import { useFlow } from './hooks/useFlow';
3129
import './index.less';
30+
import { useTemporalStore } from './hooks/useTemporalStore';
3231

3332
const CustomNode = memo(CustomNodeComponent);
3433
const edgeTypes = { buttonedge: memo(CustomEdge) };
@@ -38,40 +37,40 @@ const edgeTypes = { buttonedge: memo(CustomEdge) };
3837
* XFlow 入口
3938
*
4039
*/
41-
const XFlow: FC<FlowProps> = memo(props => {
42-
const { initialValues, settings } = props;
40+
const XFlow: FC<FlowProps> = memo(() => {
4341
const workflowContainerRef = useRef<HTMLDivElement>(null);
4442
const store = useStoreApi();
4543
const { zoomTo } = useReactFlow();
4644
const {
4745
layout,
4846
nodes,
4947
edges,
48+
setNodes,
49+
setEdges,
5050
panOnDrag,
5151
onNodesChange,
5252
onEdgesChange,
5353
onConnect,
54-
setLayout,
5554
setCandidateNode,
5655
setMousePosition,
5756
} = useStore(
58-
state => ({
59-
nodes: state.nodes,
60-
edges: state.edges,
61-
layout: state.layout,
62-
panOnDrag: state.panOnDrag,
63-
setLayout: state.setLayout,
64-
setMousePosition: state.setMousePosition,
65-
setCandidateNode: state.setCandidateNode,
66-
onNodesChange: state.onNodesChange,
67-
onEdgesChange: state.onEdgesChange,
68-
onConnect: state.onConnect,
57+
s => ({
58+
nodes: s.nodes,
59+
edges: s.edges,
60+
setNodes: s.setNodes,
61+
setEdges: s.setEdges,
62+
layout: s.layout,
63+
panOnDrag: s.panOnDrag,
64+
setMousePosition: s.setMousePosition,
65+
setCandidateNode: s.setCandidateNode,
66+
onNodesChange: s.onNodesChange,
67+
onEdgesChange: s.onEdgesChange,
68+
onConnect: s.onConnect,
6969
}),
7070
shallow
7171
);
72-
const { setNodes, setEdges } = useFlow();
72+
const { record } = useTemporalStore();
7373
const [activeNode, setActiveNode] = useState<any>(null);
74-
const temporalStore = useTemporalStore();
7574
useEffect(() => {
7675
zoomTo(0.8);
7776
setAutoFreeze(false);
@@ -80,14 +79,6 @@ const XFlow: FC<FlowProps> = memo(props => {
8079
};
8180
}, []);
8281

83-
useEffect(() => {
84-
setLayout(props.layout);
85-
// TODO: 默认关闭时间机器,可以向 zundo 贡献一个配置
86-
temporalStore.pause();
87-
setNodes(transformNodes(initialValues?.nodes));
88-
setEdges(initialValues?.edges);
89-
}, []);
90-
9182
useEventListener('keydown', e => {
9283
if ((e.key === 'd' || e.key === 'D') && (e.ctrlKey || e.metaKey))
9384
e.preventDefault();
@@ -149,7 +140,6 @@ const XFlow: FC<FlowProps> = memo(props => {
149140
// y: 0,
150141
// },
151142
// };
152-
// // record(() => {
153143
// addNodes(newNode);
154144
// addEdges({
155145
// id: uuid(),
@@ -160,7 +150,6 @@ const XFlow: FC<FlowProps> = memo(props => {
160150
// updateEdge(targetEdge?.id as string, {
161151
// source: newNode.id,
162152
// });
163-
// // });
164153
// };
165154

166155
// edge 移入/移出效果
@@ -213,12 +202,6 @@ const XFlow: FC<FlowProps> = memo(props => {
213202
};
214203
}, [layout]);
215204

216-
// const edgeTypes = { buttonedge: (edgeProps: any) => <CustomEdge layout={layout} {...edgeProps} /> };
217-
// const { icon, description } =
218-
// settings.find(
219-
// item => item.type?.toLowerCase() === activeNode?.node?.toLowerCase()
220-
// ) || {};
221-
222205
const NodeEditorWrap = useMemo(() => {
223206
return (
224207
<NodeEditor
@@ -250,7 +233,15 @@ const XFlow: FC<FlowProps> = memo(props => {
250233
}}
251234
onConnect={onConnect}
252235
onNodesChange={changes => {
253-
onNodesChange(changes);
236+
changes.forEach(change => {
237+
if (change.type === 'remove' || change.type === 'add') {
238+
record(() => {
239+
onNodesChange(changes);
240+
})
241+
} else {
242+
onNodesChange(changes);
243+
}
244+
})
254245
}}
255246
onEdgesChange={changes => {
256247
onEdgesChange(changes);
@@ -261,6 +252,9 @@ const XFlow: FC<FlowProps> = memo(props => {
261252
onEdgeMouseLeave={(_, edge) => {
262253
getUpdateEdgeConfig(edge, '#c9c9c9');
263254
}}
255+
onNodesDelete={() => {
256+
// setActiveNode(null);
257+
}}
264258
>
265259
<CandidateNode />
266260
<Operator addNode={handleAddNode} />

packages/x-flow/src/components/CandidateNode/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ const CandidateNode = () => {
4141
},
4242
position: { x, y },
4343
};
44-
// @ts-ignore
4544
addNodes(newNodes);
4645
setCandidateNode(null);
4746
});

packages/x-flow/src/components/FlowProvider/index.tsx

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
import { useStore } from '../../hooks/useStore';
12
import StoreContext, { Provider } from '../../models/context';
23
import { createStore } from '../../models/store';
4+
import { transformNodes } from '../../utils';
35

46
import type { ReactNode } from 'react';
5-
import React, { memo, useContext, useState } from 'react';
7+
import React, { memo, useContext, useEffect, useState } from 'react';
68

79
export const FlowProvider = memo<{
8-
initialNodes: any[];
9-
initialEdges: any[];
10+
initialNodes?: any[];
11+
initialEdges?: any[];
1012
children: ReactNode;
1113
}>(({ initialNodes: nodes = [], initialEdges: edges = [], children }) => {
1214
const [store] = useState(() =>
@@ -19,25 +21,46 @@ export const FlowProvider = memo<{
1921
return <Provider value={store}>{children}</Provider>;
2022
});
2123

24+
const InitialProvider = ({ nodes, edges, layout, children }) => {
25+
const { setNodes, setEdges, setLayout } = useStore(s => ({
26+
setNodes: s.setNodes,
27+
setEdges: s.setEdges,
28+
setLayout: s.setLayout,
29+
}));
30+
useEffect(() => {
31+
setNodes(transformNodes(nodes));
32+
setLayout(layout);
33+
setEdges(edges);
34+
}, []);
35+
36+
// we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
37+
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
38+
return <>{children}</>;
39+
};
40+
2241
export const FlowProviderWrapper = ({
2342
children,
2443
nodes,
2544
edges,
45+
layout,
2646
}: {
2747
children: React.ReactNode;
2848
nodes: any[];
2949
edges: any[];
50+
layout?: 'LR' | 'TB';
3051
}) => {
3152
const isWrapped = useContext(StoreContext);
3253

3354
if (isWrapped) {
34-
// we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
35-
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
36-
return <>{children}</>;
55+
return (
56+
<InitialProvider nodes={nodes} edges={edges} layout={layout}>
57+
{children}
58+
</InitialProvider>
59+
);
3760
}
3861

3962
return (
40-
<FlowProvider initialNodes={nodes} initialEdges={edges}>
63+
<FlowProvider initialNodes={transformNodes(nodes)} initialEdges={edges}>
4164
{children}
4265
</FlowProvider>
4366
);
Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,37 @@
1-
import { useMemo } from 'react';
1+
import { Edge } from '@xyflow/react';
22
import { useMemoizedFn } from 'ahooks';
3-
import { useStoreApi } from './useStore';
4-
import { useTemporalStore } from './useTemporalStore';
3+
import { useMemo } from 'react';
54
import { FlowNode } from '../models/store';
6-
import { Edge } from '@xyflow/react';
5+
import { useStoreApi } from './useStore';
76

87
export const useFlow = () => {
98
const storeApi = useStoreApi();
109
const instance = storeApi.getState();
11-
const temporalStore = useTemporalStore();
1210

1311
const getNodes = useMemoizedFn(() => storeApi.getState().nodes);
1412
const getEdges = useMemoizedFn(() => storeApi.getState().edges);
1513
const setNodes = useMemoizedFn((nodes: FlowNode[]) => {
16-
temporalStore.record(() => {
17-
storeApi.getState().setNodes(nodes);
18-
})
19-
})
14+
storeApi.getState().setNodes(nodes);
15+
});
2016
const addNodes = useMemoizedFn((nodes: FlowNode[]) => {
21-
temporalStore.record(() => {
22-
storeApi.getState().addNodes(nodes);
23-
})
24-
})
17+
storeApi.getState().addNodes(nodes);
18+
});
2519
const setEdges = useMemoizedFn((edges: Edge[]) => {
2620
storeApi.getState().setEdges(edges);
27-
})
21+
});
2822
const addEdges = useMemoizedFn((edges: Edge[]) => {
2923
storeApi.getState().addEdges(edges);
30-
})
24+
});
3125

32-
return useMemo(() => ({
33-
setNodes,
34-
addNodes,
35-
setEdges,
36-
addEdges,
37-
getNodes,
38-
getEdges,
39-
}), [instance]);
26+
return useMemo(
27+
() => ({
28+
setNodes,
29+
addNodes,
30+
setEdges,
31+
addEdges,
32+
getNodes,
33+
getEdges,
34+
}),
35+
[instance]
36+
);
4037
};

packages/x-flow/src/hooks/useTemporalStore.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ export const useTemporalStore = () => {
99
'[XFlow]: Seems like you have not used zustand provider as an ancestor.'
1010
);
1111
}
12+
const temporalStore = store.temporal.getState();
13+
// 默认关闭时间机器
14+
temporalStore.pause();
1215

1316
return {
1417
...store.temporal.getState(),
1518
record: (callback: () => void) => {
16-
const temporalStore = store.temporal.getState();
1719
temporalStore.resume();
1820
callback();
1921
temporalStore.pause();

packages/x-flow/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import XFlow from './XFlow';
22
import withProvider from './withProvider';
33

44
import * as nodes from './nodes';
5+
import FlowProps from './types';
56

67
export type {
78
default as FR,
@@ -12,4 +13,4 @@ export { useFlow } from './hooks/useFlow';
1213
export { useNodes } from './hooks/useNodes';
1314
export { useEdges } from './hooks/useEdges';
1415

15-
export default withProvider(XFlow, nodes);
16+
export default withProvider<FlowProps>(XFlow, nodes);

0 commit comments

Comments
 (0)