fix(useReactFlow): use onNodesChange and onEdgesChange
This commit is contained in:
@@ -33,7 +33,7 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
const onConnect = (params: Connection | Edge) => setEdges((eds) => addEdge(params, eds));
|
const onConnect = (params: Connection | Edge) => setEdges((eds) => addEdge(params, eds));
|
||||||
const { project, setCenter, zoomIn, zoomOut, fitView } = useReactFlow();
|
const { project, setCenter, zoomIn, zoomOut, fitView, addNodes, setNodes: setNodesHook } = useReactFlow();
|
||||||
|
|
||||||
const onPaneClick = useCallback(
|
const onPaneClick = useCallback(
|
||||||
(evt) => {
|
(evt) => {
|
||||||
@@ -49,7 +49,7 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[project]
|
[project, setNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onNodeClick = useCallback(
|
const onNodeClick = useCallback(
|
||||||
@@ -60,6 +60,22 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
[setCenter]
|
[setCenter]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onAddNode = useCallback(() => {
|
||||||
|
const newNode = {
|
||||||
|
id: getId(),
|
||||||
|
position: { x: Math.random() * 500, y: Math.random() * 500 },
|
||||||
|
data: {
|
||||||
|
label: 'New Node',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
addNodes(newNode);
|
||||||
|
}, [addNodes]);
|
||||||
|
|
||||||
|
const onResetNodes = useCallback(() => {
|
||||||
|
setNodesHook(initialNodes);
|
||||||
|
}, [setNodesHook]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
@@ -77,6 +93,8 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
<button onClick={() => zoomIn({ duration: 1200 })}>zoomIn</button>
|
<button onClick={() => zoomIn({ duration: 1200 })}>zoomIn</button>
|
||||||
<button onClick={() => zoomOut({ duration: 0 })}>zoomOut</button>
|
<button onClick={() => zoomOut({ duration: 0 })}>zoomOut</button>
|
||||||
<button onClick={() => fitView({ duration: 1200, padding: 0.3 })}>fitView</button>
|
<button onClick={() => fitView({ duration: 1200, padding: 0.3 })}>fitView</button>
|
||||||
|
<button onClick={onAddNode}>add node</button>
|
||||||
|
<button onClick={onResetNodes}>reset nodes</button>
|
||||||
</div>
|
</div>
|
||||||
<Background />
|
<Background />
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
+16
-18
@@ -31,11 +31,9 @@ import Undirectional from './Undirectional';
|
|||||||
import UpdatableEdge from './UpdatableEdge';
|
import UpdatableEdge from './UpdatableEdge';
|
||||||
import UpdateNode from './UpdateNode';
|
import UpdateNode from './UpdateNode';
|
||||||
import UseUpdateNodeInternals from './UseUpdateNodeInternals';
|
import UseUpdateNodeInternals from './UseUpdateNodeInternals';
|
||||||
import UseZoomPanHelper from './UseZoomPanHelper';
|
import UseReactFlow from './UseReactFlow';
|
||||||
import Validation from './Validation';
|
import Validation from './Validation';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
@@ -146,8 +144,8 @@ const routes = [
|
|||||||
component: UpdateNode,
|
component: UpdateNode,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/usezoompanhelper',
|
path: '/usereactflow',
|
||||||
component: UseZoomPanHelper,
|
component: UseReactFlow,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/useupdatenodeinternals',
|
path: '/useupdatenodeinternals',
|
||||||
@@ -169,18 +167,18 @@ const Header = () => {
|
|||||||
const onChange = (event: ChangeEvent<HTMLSelectElement>) => navigate(event.target.value);
|
const onChange = (event: ChangeEvent<HTMLSelectElement>) => navigate(event.target.value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
<a className="logo" href="https://github.com/wbkd/react-flow">
|
<a className="logo" href="https://github.com/wbkd/react-flow">
|
||||||
React Flow Dev
|
React Flow Dev
|
||||||
</a>
|
</a>
|
||||||
<select defaultValue={location.pathname} onChange={onChange}>
|
<select defaultValue={location.pathname} onChange={onChange}>
|
||||||
{routes.map((route) => (
|
{routes.map((route) => (
|
||||||
<option value={route.path} key={route.path}>
|
<option value={route.path} key={route.path}>
|
||||||
{route.path === '/' ? 'overview' : route.path.substring(1, route.path.length)}
|
{route.path === '/' ? 'overview' : route.path.substring(1, route.path.length)}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -189,7 +187,7 @@ ReactDOM.render(
|
|||||||
<Header />
|
<Header />
|
||||||
<Routes>
|
<Routes>
|
||||||
{routes.map((route) => (
|
{routes.map((route) => (
|
||||||
<Route path={route.path} key={route.path} element={<route.component />}/>
|
<Route path={route.path} key={route.path} element={<route.component />} />
|
||||||
))}
|
))}
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>,
|
</BrowserRouter>,
|
||||||
|
|||||||
+35
-11
@@ -2,7 +2,7 @@ import { useCallback } from 'react';
|
|||||||
|
|
||||||
import useViewportHelper from './useViewportHelper';
|
import useViewportHelper from './useViewportHelper';
|
||||||
import { useStoreApi } from '../store';
|
import { useStoreApi } from '../store';
|
||||||
import { ReactFlowInstance, Instance } from '../types';
|
import { ReactFlowInstance, Instance, NodeAddChange, EdgeAddChange, NodeResetChange, EdgeResetChange } from '../types';
|
||||||
|
|
||||||
export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> {
|
export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlowInstance<NodeData, EdgeData> {
|
||||||
const { initialized: viewportInitialized, ...viewportHelperFunctions } = useViewportHelper();
|
const { initialized: viewportInitialized, ...viewportHelperFunctions } = useViewportHelper();
|
||||||
@@ -30,30 +30,54 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const setNodes = useCallback<Instance.SetNodes<NodeData>>((payload) => {
|
const setNodes = useCallback<Instance.SetNodes<NodeData>>((payload) => {
|
||||||
const { nodeInternals, setNodes } = store.getState();
|
const { nodeInternals, setNodes, hasDefaultNodes, onNodesChange } = store.getState();
|
||||||
const nodes = Array.from(nodeInternals.values());
|
const nodes = Array.from(nodeInternals.values());
|
||||||
const nextNodes = typeof payload === 'function' ? payload(nodes) : payload;
|
const nextNodes = typeof payload === 'function' ? payload(nodes) : payload;
|
||||||
setNodes(nextNodes);
|
|
||||||
|
if (hasDefaultNodes) {
|
||||||
|
setNodes(nextNodes);
|
||||||
|
} else if (onNodesChange) {
|
||||||
|
const changes = nextNodes.map((node) => ({ item: node, type: 'reset' } as NodeResetChange<NodeData>));
|
||||||
|
onNodesChange(changes);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const setEdges = useCallback<Instance.SetEdges<EdgeData>>((payload) => {
|
const setEdges = useCallback<Instance.SetEdges<EdgeData>>((payload) => {
|
||||||
const { edges = [], setEdges } = store.getState();
|
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange } = store.getState();
|
||||||
const nextEdges = typeof payload === 'function' ? payload(edges) : payload;
|
const nextEdges = typeof payload === 'function' ? payload(edges) : payload;
|
||||||
setEdges(nextEdges);
|
|
||||||
|
if (hasDefaultEdges) {
|
||||||
|
setEdges(nextEdges);
|
||||||
|
} else if (onEdgesChange) {
|
||||||
|
const changes = nextEdges.map((edge) => ({ item: edge, type: 'reset' } as EdgeResetChange<EdgeData>));
|
||||||
|
onEdgesChange(changes);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addNodes = useCallback<Instance.AddNodes<NodeData>>((payload) => {
|
const addNodes = useCallback<Instance.AddNodes<NodeData>>((payload) => {
|
||||||
const nodes = Array.isArray(payload) ? payload : [payload];
|
const nodes = Array.isArray(payload) ? payload : [payload];
|
||||||
const { nodeInternals, setNodes } = store.getState();
|
const { nodeInternals, setNodes, hasDefaultNodes, onNodesChange } = store.getState();
|
||||||
const currentNodes = Array.from(nodeInternals.values());
|
|
||||||
const nextNodes = [...currentNodes, ...nodes];
|
if (hasDefaultNodes) {
|
||||||
setNodes(nextNodes);
|
const currentNodes = Array.from(nodeInternals.values());
|
||||||
|
const nextNodes = [...currentNodes, ...nodes];
|
||||||
|
setNodes(nextNodes);
|
||||||
|
} else if (onNodesChange) {
|
||||||
|
const changes = nodes.map((node) => ({ item: node, type: 'add' } as NodeAddChange<NodeData>));
|
||||||
|
onNodesChange(changes);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addEdges = useCallback<Instance.AddEdges<EdgeData>>((payload) => {
|
const addEdges = useCallback<Instance.AddEdges<EdgeData>>((payload) => {
|
||||||
const nextEdges = Array.isArray(payload) ? payload : [payload];
|
const nextEdges = Array.isArray(payload) ? payload : [payload];
|
||||||
const { edges = [], setEdges } = store.getState();
|
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange } = store.getState();
|
||||||
setEdges([...edges, ...nextEdges]);
|
|
||||||
|
if (hasDefaultEdges) {
|
||||||
|
setEdges([...edges, ...nextEdges]);
|
||||||
|
} else if (onEdgesChange) {
|
||||||
|
const changes = nextEdges.map((edge) => ({ item: edge, type: 'add' } as EdgeAddChange<EdgeData>));
|
||||||
|
onEdgesChange(changes);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toObject = useCallback<Instance.ToObject<NodeData, EdgeData>>(() => {
|
const toObject = useCallback<Instance.ToObject<NodeData, EdgeData>>(() => {
|
||||||
|
|||||||
+28
-3
@@ -1,5 +1,6 @@
|
|||||||
import { XYPosition, Dimensions } from './utils';
|
import { XYPosition, Dimensions } from './utils';
|
||||||
import { NodeHandleBounds } from './nodes';
|
import { NodeHandleBounds, Node } from './nodes';
|
||||||
|
import { Edge } from './edges';
|
||||||
|
|
||||||
export type NodeDimensionChange = {
|
export type NodeDimensionChange = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -26,8 +27,32 @@ export type NodeRemoveChange = {
|
|||||||
type: 'remove';
|
type: 'remove';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NodeChange = NodeDimensionChange | NodePositionChange | NodeSelectionChange | NodeRemoveChange;
|
export type NodeAddChange<NodeData = any> = {
|
||||||
|
item: Node<NodeData>;
|
||||||
|
type: 'add';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type NodeResetChange<NodeData = any> = {
|
||||||
|
item: Node<NodeData>;
|
||||||
|
type: 'reset';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type NodeChange =
|
||||||
|
| NodeDimensionChange
|
||||||
|
| NodePositionChange
|
||||||
|
| NodeSelectionChange
|
||||||
|
| NodeRemoveChange
|
||||||
|
| NodeAddChange
|
||||||
|
| NodeResetChange;
|
||||||
|
|
||||||
export type EdgeSelectionChange = NodeSelectionChange;
|
export type EdgeSelectionChange = NodeSelectionChange;
|
||||||
export type EdgeRemoveChange = NodeRemoveChange;
|
export type EdgeRemoveChange = NodeRemoveChange;
|
||||||
export type EdgeChange = EdgeSelectionChange | EdgeRemoveChange;
|
export type EdgeAddChange<EdgeData = any> = {
|
||||||
|
item: Edge<EdgeData>;
|
||||||
|
type: 'add';
|
||||||
|
};
|
||||||
|
export type EdgeResetChange<EdgeData = any> = {
|
||||||
|
item: Edge<EdgeData>;
|
||||||
|
type: 'reset';
|
||||||
|
};
|
||||||
|
export type EdgeChange = EdgeSelectionChange | EdgeRemoveChange | EdgeAddChange | EdgeResetChange;
|
||||||
|
|||||||
@@ -44,8 +44,14 @@ function handleParentExpand(res: any[], updateItem: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyChanges(changes: NodeChange[] | EdgeChange[], elements: any[]): any[] {
|
function applyChanges(changes: any[], elements: any[]): any[] {
|
||||||
const initElements: any[] = [];
|
// unfortunately we need this hack to handle the setNodes and setEdges function of the
|
||||||
|
// useReactFlow hook.
|
||||||
|
if (changes.some((c) => c.type === 'reset')) {
|
||||||
|
return changes.filter((c) => c.type === 'reset').map((c) => c.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
const initElements: any[] = changes.filter((c) => c.type === 'add').map((c) => c.item);
|
||||||
|
|
||||||
return elements.reduce((res: any[], item: any) => {
|
return elements.reduce((res: any[], item: any) => {
|
||||||
const currentChange = changes.find((c) => c.id === item.id);
|
const currentChange = changes.find((c) => c.id === item.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user