Preventing Cycles
In the validation example, we saw how to use
the isValidConnection
callback
to prevent certain connections from being created. This example shows how to use
the getOutgoers
util to check if a new
connection would cause a cycle in the flow.
import React, { useCallback } from 'react';
import {
ReactFlow,
Background,
useNodesState,
useEdgesState,
addEdge,
getOutgoers,
useReactFlow,
ReactFlowProvider,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { nodes as initialNodes, edges as initialEdges } from './initialElements';
const Flow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const { getNodes, getEdges } = useReactFlow();
const isValidConnection = useCallback(
(connection) => {
// we are using getNodes and getEdges helpers here
// to make sure we create isValidConnection function only once
const nodes = getNodes();
const edges = getEdges();
const target = nodes.find((node) => node.id === connection.target);
const hasCycle = (node, visited = new Set()) => {
if (visited.has(node.id)) return false;
visited.add(node.id);
for (const outgoer of getOutgoers(node, nodes, edges)) {
if (outgoer.id === connection.source) return true;
if (hasCycle(outgoer, visited)) return true;
}
};
if (target.id === connection.source) return false;
return !hasCycle(target);
},
[getNodes, getEdges],
);
const onConnect = useCallback((params) =>
setEdges((els) => addEdge(params, els)),
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
isValidConnection={isValidConnection}
fitView
style={{ backgroundColor: "#F7F9FB" }}
>
<Background />
</ReactFlow>
);
};
export default () => (
<ReactFlowProvider>
<Flow />
</ReactFlowProvider>
);