Advanced React Flow patterns for complex use cases. Use when implementing sub-flows, custom connection lines, programmatic layouts, drag-and-drop, undo/redo, or complex state synchronization.
tsx
const nodes = [
// 父级(分组)节点
{
id: group-1,
type: group,
position: { x: 0, y: 0 },
style: { width: 400, height: 300, padding: 10 },
data: { label: 分组 },
},
// 子节点
{
id: child-1,
parentId: group-1, // 引用父节点
extent: parent, // 限制在父节点范围内
expandParent: true, // 拖拽到边缘时自动扩展父节点
position: { x: 20, y: 50 }, // 相对于父节点的位置
data: { label: 子节点 1 },
},
{
id: child-2,
parentId: group-1,
extent: parent,
position: { x: 200, y: 50 },
data: { label: 子节点 2 },
},
];
tsx
function GroupNode({ data, id }: NodeProps) {
return (
tsx
import { ConnectionLineComponentProps, getSmoothStepPath } from @xyflow/react;
function CustomConnectionLine({
fromX, fromY, fromPosition,
toX, toY, toPosition,
connectionStatus,
}: ConnectionLineComponentProps) {
const [path] = getSmoothStepPath({
sourceX: fromX,
sourceY: fromY,
sourcePosition: fromPosition,
targetX: toX,
targetY: toY,
targetPosition: toPosition,
});
return (
fill=none
stroke={connectionStatus === valid ? #22c55e : #ef4444}
strokeWidth={2}
strokeDasharray=5 5
/>
);
}
tsx
import { useReactFlow, useCallback, useRef } from react;
function DnDFlow() {
const reactFlowWrapper = useRef(null);
const { screenToFlowPosition, addNodes } = useReactFlow();
const [reactFlowInstance, setReactFlowInstance] = useState(null);
const onDragOver = useCallback((event: DragEvent) => {
event.preventDefault();
event.dataTransfer.dropEffect = move;
}, []);
const onDrop = useCallback((event: DragEvent) => {
event.preventDefault();
const type = event.dataTransfer.getData(application/reactflow);
if (!type) return;
// 将屏幕位置转换为流程位置
const position = screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
const newNode = {
id: ${Date.now()},
type,
position,
data: { label: ${type} 节点 },
};
addNodes(newNode);
}, [screenToFlowPosition, addNodes]);
return (
// 侧边栏组件
function Sidebar() {
const onDragStart = (event: DragEvent, nodeType: string) => {
event.dataTransfer.setData(application/reactflow, nodeType);
event.dataTransfer.effectAllowed = move;
};
return (
);
}
tsx
import { useCallback, useState } from react;
function useUndoRedo
const [history, setHistory] = useState
const [index, setIndex] = useState(0);
const state = history[index];
const setState = useCallback((newState: T | ((prev: T) => T)) => {
setHistory((prev) => {
const resolved = typeof newState === function
? (newState as (prev: T) => T)(prev[index])
: newState;
// 移除未来状态并添加新状态
const newHistory = prev.slice(0, index + 1);
return [...newHistory, resolved];
});
setIndex((i) => i + 1);
}, [index]);
const undo = useCallback(() => {
setIndex((i) => Math.max(0, i - 1));
}, []);
const redo = useCallback(() => {
setIndex((i) => Math.min(history.length - 1, i + 1));
}, [history.length]);
const canUndo = index > 0;
const canRedo = index < history.length - 1;
return { state, setState, undo, redo, canUndo, canRedo };
}
// 使用示例
function Flow() {
const {
state: { nodes, edges },
setState,
undo, redo, canUndo, canRedo
} = useUndoRedo({ nodes: initialNodes, edges: initialEdges });
// 在重要变更时捕获状态
const onNodesChange = useCallback((changes) => {
const hasPositionChange = changes.some(c => c.type === position && !c.dragging);
if (hasPositionChange) {
setState(prev => ({
nodes: applyNodeChanges(changes, prev.nodes),
edges: prev.edges,
}));
}
}, [setState]);
}
tsx
import dagre from dagre;
interface LayoutOptions {
direction: TB | BT | LR | RL;
nodeWidth: number;
nodeHeight: number;
}
function getLayoutedElements(
nodes: Node[],
edges: Edge[],
options: LayoutOptions = { direction: TB, nodeWidth: 172, nodeHeight: 36 }
) {
const g = new dagre.graphlib.Graph();
g.setGraph({ rankdir: options.direction });
g.setDefaultEdgeLabel(() => ({}));
nodes.forEach((node) => {
g.setNode(node.id, {
width: node.measured?.width ?? options.nodeWidth,
height: node.measured?.height ?? options.nodeHeight,
});
});
edges.forEach((edge) => {
g.setEdge(edge.source, edge.target);
});
dagre.layout(g);
const layoutedNodes = nodes.map((node) => {
const nodeWithPosition = g.node(node.id);
return {
...node,
position: {
x: nodeWithPosition.x - (node.measured?.width ?? options.nodeWidth) / 2,
y: nodeWithPosition.y - (node.measured?.height ?? options.nodeHeight) / 2,
},
};
});
return { nodes: layoutedNodes, edges };
}
// 在节点测量后使用
function Flow() {
const { fitView } = useReactFlow();
const onLayout = useCallback((direction: TB | LR) => {
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
nodes,
edges,
{ direction, nodeWidth: 150, nodeHeight: 50 }
);
setNodes([...layoutedNodes]);
setEdges([...layoutedEdges]);
window.requestAnimationFrame(() => {
fitView({ duration: 500 });
});
}, [nodes, edges, setNodes, setEdges, fitView]);
}
tsx
function Flow() {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const { screenToFlowPosition } = useReactFlow();
const onConnectEnd = useCallback(
(event: MouseEvent | TouchEvent, connectionState: FinalConnectionState) => {
// 仅在放置到画布上(而非节点上)时继续
if (!connectionState.isValid && connectionState.fromHandle) {
const id = ${Date.now()
该技能支持在以下平台通过对话安装:
帮我安装 SkillHub 和 react-flow-advanced-1775982662 技能
设置 SkillHub 为我的优先技能安装源,然后帮我安装 react-flow-advanced-1775982662 技能
skillhub install react-flow-advanced-1775982662
文件大小: 4.25 KB | 发布时间: 2026-4-13 11:44