React Flow Architecture

existential-birds/beagle/plugins/beagle-react/skills/react-flow-architecture

作者 existential-birdsd1a74899fbfec74974d1818e4cac7c3d54d44b65無授權條款83 個星標收錄於 2026年10月9日更新於 2026年10月9日儲存庫3 週前更新

Architectural guidance for building node-based UIs with React Flow. Use when designing flow-based applications, making decisions about state management, integration patterns, or evaluating whether React Flow fits a use case.

AI 產生的概覽

使用 React Flow 打造節點式介面的架構指引,涵蓋適用性、狀態、整合與效能。

功能
這項技能為使用 React Flow 建立節點式使用者介面提供架構指引。它協助判斷 React Flow 是否適合某個使用情境、選擇狀態管理方式、規劃資料流,並處理視埠保存、後端整合、版面演算法與效能擴充。它產出的是設計決策與建議,而非程式碼成品。
適用情境
在設計流程式應用,或評估 React Flow 是否適合某個使用情境時使用。它著重於實作之前關於狀態管理、整合模式與擴充性的決策。
執行需求
不需要指令碼或工具,僅為說明性指引。套用這些模式時需熟悉 React Flow、React,以及 Zustand、dagre 等相關函式庫。

React Flow Architecture

When to Use 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)

Decision workflow (gates)

Run this sequence before locking the stack or sprinting implementation. Skip only for throwaway prototypes.

  1. Name the interactions — List the top user actions (e.g. drag, connect, delete, group). Pass: Each action maps to a concrete React Flow callback you will implement (onNodesChange, onConnect, …).

  2. Classify scale — Estimate peak nodes (visible canvas or document total). Pass: Your range matches a row in Node Count Guidelines and you accept the listed strategy (e.g. onlyRenderVisibleElements when that row implies it).

  3. Place state — Choose local hooks, an external store, or Redux/other. Pass: One sentence states where persistence, undo, or cross-surface sync will live, or explicitly “not needed yet.”

  4. Re-check alternatives — If the use case matches Consider Alternatives, Pass: One sentence explains why React Flow still fits or which listed alternative you chose instead.

Architecture Patterns

Package Structure (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.

State Management Approaches

1. Local State (Simple Apps)
tsx
// useNodesState/useEdgesState for prototypingconst [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);

Pros: Simple, minimal boilerplate Cons: State isolated to component tree

2. External Store (Production)
tsx
// Zustand store exampleimport { 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 componentfunction 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

3. Redux/Other State Libraries
tsx
// Connect via selectorsconst 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

Sub-Flow Pattern (Nested Nodes)

tsx
// Parent node containing child nodesconst 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

Viewport Persistence

tsx
// Save viewport stateconst { 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

tsx
// Load from APIuseEffect(() => {  fetch('/api/flow')    .then(r => r.json())    .then(({ nodes, edges }) => {      setNodes(nodes);      setEdges(edges);    });}, []);
// Debounced auto-saveconst 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,  };}

Performance Scaling

Node Count Guidelines

NodesStrategy
< 100Default settings
100-500Enable onlyRenderVisibleElements
500-1000Simplify custom nodes, reduce DOM elements
> 1000Consider virtualization, WebGL alternatives

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}/>

Trade-offs

Controlled vs Uncontrolled

ControlledUncontrolled
More boilerplateLess code
Full state controlInternal state
Easy persistenceNeed toObject()
Better for complex appsGood for prototypes

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} />

Edge Rendering

Default edgesCustom edges
Fast renderingMore control
Limited stylingAny SVG/HTML
Simple use casesComplex labels

來源與署名

來源:existential-birds/beagle位於plugins/beagle-react/skills/react-flow-architecture提交d1a7489

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架