react-flow-architecture

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Flow Architecture

React Flow 架构设计

When to Use React Flow

何时使用React Flow

Good Fit

适用场景

  • Visual programming interfaces
  • Workflow builders and automation tools
  • Diagram editors (flowcharts, org charts)
  • Data pipeline visualization
  • Mind mapping tools
  • Node-based audio/video editors
  • Decision tree builders
  • State machine designers
  • 可视化编程界面
  • 工作流构建器与自动化工具
  • 图编辑器(流程图、组织结构图)
  • 数据管道可视化
  • 思维导图工具
  • 基于节点的音视频编辑器
  • 决策树构建器
  • 状态机设计器

Consider Alternatives

考虑替代方案

  • Simple static diagrams (use SVG or canvas directly)
  • Heavy real-time collaboration (may need custom sync layer)
  • 3D visualizations (use Three.js, react-three-fiber)
  • Graph analysis with 10k+ nodes (use WebGL-based solutions like Sigma.js)
  • 简单静态图表(直接使用SVG或canvas)
  • 重度实时协作场景(可能需要自定义同步层)
  • 3D可视化(使用Three.js、react-three-fiber)
  • 包含10000+节点的图分析(使用基于WebGL的解决方案如Sigma.js)

Architecture Patterns

架构模式

Package Structure (xyflow)

包结构(xyflow)

@xyflow/system (vanilla TypeScript)
├── Core algorithms (edge paths, bounds, viewport)
├── xypanzoom (d3-based pan/zoom)
├── xydrag, xyhandle, xyminimap, xyresizer
└── Shared types

@xyflow/react (depends on @xyflow/system)
├── React components and hooks
├── Zustand store for state management
└── Framework-specific integrations

@xyflow/svelte (depends on @xyflow/system)
└── Svelte components and stores
Implication: Core logic is framework-agnostic. When contributing or debugging, check if issue is in @xyflow/system or framework-specific package.
@xyflow/system (vanilla TypeScript)
├── Core algorithms (edge paths, bounds, viewport)
├── xypanzoom (d3-based pan/zoom)
├── xydrag, xyhandle, xyminimap, xyresizer
└── Shared types

@xyflow/react (depends on @xyflow/system)
├── React components and hooks
├── Zustand store for state management
└── Framework-specific integrations

@xyflow/svelte (depends on @xyflow/system)
└── Svelte components and stores
提示:核心逻辑与框架无关。在贡献代码或调试时,需检查问题是出在@xyflow/system还是特定框架的包中。

State Management Approaches

状态管理方案

1. Local State (Simple Apps)

1. 本地状态(简单应用)

tsx
// useNodesState/useEdgesState for prototyping
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
Pros: Simple, minimal boilerplate Cons: State isolated to component tree
tsx
// useNodesState/useEdgesState for prototyping
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
优点:简单,样板代码少 缺点:状态隔离在组件树中

2. External Store (Production)

2. 外部状态库(生产环境)

tsx
// Zustand store example
import { create } from 'zustand';

interface FlowStore {
  nodes: Node[];
  edges: Edge[];
  setNodes: (nodes: Node[]) => void;
  onNodesChange: OnNodesChange;
}

const useFlowStore = create<FlowStore>((set, get) => ({
  nodes: initialNodes,
  edges: initialEdges,
  setNodes: (nodes) => set({ nodes }),
  onNodesChange: (changes) => {
    set({ nodes: applyNodeChanges(changes, get().nodes) });
  },
}));

// In component
function Flow() {
  const { nodes, edges, onNodesChange } = useFlowStore();
  return <ReactFlow nodes={nodes} onNodesChange={onNodesChange} />;
}
Pros: State accessible anywhere, easier persistence/sync Cons: More setup, need careful selector optimization
tsx
// Zustand store example
import { create } from 'zustand';

interface FlowStore {
  nodes: Node[];
  edges: Edge[];
  setNodes: (nodes: Node[]) => void;
  onNodesChange: OnNodesChange;
}

const useFlowStore = create<FlowStore>((set, get) => ({
  nodes: initialNodes,
  edges: initialEdges,
  setNodes: (nodes) => set({ nodes }),
  onNodesChange: (changes) => {
    set({ nodes: applyNodeChanges(changes, get().nodes) });
  },
}));

// In component
function Flow() {
  const { nodes, edges, onNodesChange } = useFlowStore();
  return <ReactFlow nodes={nodes} onNodesChange={onNodesChange} />;
}
优点:状态可在任意位置访问,更易实现持久化/同步 缺点:设置步骤更多,需要谨慎优化选择器

3. Redux/Other State Libraries

3. Redux/其他状态管理库

tsx
// Connect via selectors
const nodes = useSelector(selectNodes);
const dispatch = useDispatch();

const onNodesChange = useCallback((changes: NodeChange[]) => {
  dispatch(nodesChanged(changes));
}, [dispatch]);
tsx
// Connect via selectors
const nodes = useSelector(selectNodes);
const dispatch = useDispatch();

const onNodesChange = useCallback((changes: NodeChange[]) => {
  dispatch(nodesChanged(changes));
}, [dispatch]);

Data Flow Architecture

数据流架构

User Input → Change Event → Reducer/Handler → State Update → Re-render
[Drag node] → onNodesChange → applyNodeChanges → setNodes → ReactFlow
[Connect]   → onConnect → addEdge → setEdges → ReactFlow
[Delete]    → onNodesDelete → deleteElements → setNodes/setEdges → ReactFlow
用户输入 → 变更事件 → 归约器/处理器 → 状态更新 → 重新渲染
[拖拽节点] → onNodesChange → applyNodeChanges → setNodes → ReactFlow
[连接节点]   → onConnect → addEdge → setEdges → ReactFlow
[删除节点]    → onNodesDelete → deleteElements → setNodes/setEdges → ReactFlow

Sub-Flow Pattern (Nested Nodes)

子流程模式(嵌套节点)

tsx
// Parent node containing child nodes
const nodes = [
  {
    id: 'group-1',
    type: 'group',
    position: { x: 0, y: 0 },
    style: { width: 300, height: 200 },
  },
  {
    id: 'child-1',
    parentId: 'group-1',  // Key: parent reference
    extent: 'parent',      // Key: constrain to parent
    position: { x: 10, y: 30 },  // Relative to parent
    data: { label: 'Child' },
  },
];
Considerations:
  • Use
    extent: 'parent'
    to constrain dragging
  • Use
    expandParent: true
    to auto-expand parent
  • Parent z-index affects child rendering order
tsx
// Parent node containing child nodes
const nodes = [
  {
    id: 'group-1',
    type: 'group',
    position: { x: 0, y: 0 },
    style: { width: 300, height: 200 },
  },
  {
    id: 'child-1',
    parentId: 'group-1',  // Key: parent reference
    extent: 'parent',      // Key: constrain to parent
    position: { x: 10, y: 30 },  // Relative to parent
    data: { label: 'Child' },
  },
];
注意事项:
  • 使用
    extent: 'parent'
    限制拖拽范围
  • 使用
    expandParent: true
    自动展开父节点
  • 父节点的z-index会影响子节点的渲染顺序

Viewport Persistence

视口状态持久化

tsx
// Save viewport state
const { toObject, setViewport } = useReactFlow();

const handleSave = () => {
  const flow = toObject();
  // flow.nodes, flow.edges, flow.viewport
  localStorage.setItem('flow', JSON.stringify(flow));
};

const handleRestore = () => {
  const flow = JSON.parse(localStorage.getItem('flow'));
  setNodes(flow.nodes);
  setEdges(flow.edges);
  setViewport(flow.viewport);
};
tsx
// Save viewport state
const { toObject, setViewport } = useReactFlow();

const handleSave = () => {
  const flow = toObject();
  // flow.nodes, flow.edges, flow.viewport
  localStorage.setItem('flow', JSON.stringify(flow));
};

const handleRestore = () => {
  const flow = JSON.parse(localStorage.getItem('flow'));
  setNodes(flow.nodes);
  setEdges(flow.edges);
  setViewport(flow.viewport);
};

Integration Patterns

集成模式

With Backend/API

与后端/API集成

tsx
// Load from API
useEffect(() => {
  fetch('/api/flow')
    .then(r => r.json())
    .then(({ nodes, edges }) => {
      setNodes(nodes);
      setEdges(edges);
    });
}, []);

// Debounced auto-save
const debouncedSave = useMemo(
  () => debounce((nodes, edges) => {
    fetch('/api/flow', {
      method: 'POST',
      body: JSON.stringify({ nodes, edges }),
    });
  }, 1000),
  []
);

useEffect(() => {
  debouncedSave(nodes, edges);
}, [nodes, edges]);
tsx
// Load from API
useEffect(() => {
  fetch('/api/flow')
    .then(r => r.json())
    .then(({ nodes, edges }) => {
      setNodes(nodes);
      setEdges(edges);
    });
}, []);

// Debounced auto-save
const debouncedSave = useMemo(
  () => debounce((nodes, edges) => {
    fetch('/api/flow', {
      method: 'POST',
      body: JSON.stringify({ nodes, edges }),
    });
  }, 1000),
  []
);

useEffect(() => {
  debouncedSave(nodes, edges);
}, [nodes, edges]);

With Layout Algorithms

与布局算法集成

tsx
import dagre from 'dagre';

function getLayoutedElements(nodes: Node[], edges: Edge[]) {
  const g = new dagre.graphlib.Graph();
  g.setGraph({ rankdir: 'TB' });
  g.setDefaultEdgeLabel(() => ({}));

  nodes.forEach((node) => {
    g.setNode(node.id, { width: 150, height: 50 });
  });

  edges.forEach((edge) => {
    g.setEdge(edge.source, edge.target);
  });

  dagre.layout(g);

  return {
    nodes: nodes.map((node) => {
      const pos = g.node(node.id);
      return { ...node, position: { x: pos.x, y: pos.y } };
    }),
    edges,
  };
}
tsx
import dagre from 'dagre';

function getLayoutedElements(nodes: Node[], edges: Edge[]) {
  const g = new dagre.graphlib.Graph();
  g.setGraph({ rankdir: 'TB' });
  g.setDefaultEdgeLabel(() => ({}));

  nodes.forEach((node) => {
    g.setNode(node.id, { width: 150, height: 50 });
  });

  edges.forEach((edge) => {
    g.setEdge(edge.source, edge.target);
  });

  dagre.layout(g);

  return {
    nodes: nodes.map((node) => {
      const pos = g.node(node.id);
      return { ...node, position: { x: pos.x, y: pos.y } };
    }),
    edges,
  };
}

Performance Scaling

性能扩展

Node Count Guidelines

节点数量参考策略

NodesStrategy
< 100Default settings
100-500Enable
onlyRenderVisibleElements
500-1000Simplify custom nodes, reduce DOM elements
> 1000Consider virtualization, WebGL alternatives
节点数量策略
< 100默认设置
100-500启用
onlyRenderVisibleElements
500-1000简化自定义节点,减少DOM元素
> 1000考虑虚拟化、WebGL替代方案

Optimization Techniques

优化技巧

tsx
<ReactFlow
  // Only render nodes/edges in viewport
  onlyRenderVisibleElements={true}

  // Reduce node border radius (improves intersect calculations)
  nodeExtent={[[-1000, -1000], [1000, 1000]]}

  // Disable features not needed
  elementsSelectable={false}
  panOnDrag={false}
  zoomOnScroll={false}
/>
tsx
<ReactFlow
  // Only render nodes/edges in viewport
  onlyRenderVisibleElements={true}

  // Reduce node border radius (improves intersect calculations)
  nodeExtent={[[-1000, -1000], [1000, 1000]]}

  // Disable features not needed
  elementsSelectable={false}
  panOnDrag={false}
  zoomOnScroll={false}
/>

Trade-offs

权衡选择

Controlled vs Uncontrolled

受控模式 vs 非受控模式

ControlledUncontrolled
More boilerplateLess code
Full state controlInternal state
Easy persistenceNeed
toObject()
Better for complex appsGood for prototypes
受控模式非受控模式
样板代码更多代码量更少
完全控制状态内部管理状态
易实现持久化需要使用
toObject()
适合复杂应用适合原型开发

Connection Modes

连接模式

Strict (default)Loose
Source → Target onlyAny handle → any handle
Predictable behaviorMore flexible
Use for data flowsUse for diagrams
tsx
<ReactFlow connectionMode={ConnectionMode.Loose} />
严格模式(默认)宽松模式
仅支持源→目标连接任意手柄间均可连接
行为可预测灵活性更高
适用于数据流场景适用于图表场景
tsx
<ReactFlow connectionMode={ConnectionMode.Loose} />

Edge Rendering

边渲染

Default edgesCustom edges
Fast renderingMore control
Limited stylingAny SVG/HTML
Simple use casesComplex labels
默认边自定义边
渲染速度快控制能力更强
样式选项有限支持任意SVG/HTML
适合简单场景适合复杂标签需求