Portal Community

The Sync Architecture

[workflowStore] ──(nodes, edges, viewport)──► [ReactFlow component renders]
      ▲                                                    │
      │                                                    │ User interaction
      │                                          (drag, connect, delete, pan)
      │                                                    ▼
      └────────────── (onNodesChange, onEdgesChange, onConnect, onMoveEnd) ─────

WorkflowCanvas — The Sync Bridge

// WorkflowCanvas.tsx (simplified)
import ReactFlow, { useReactFlow } from 'reactflow';
import { useWorkflowStore } from '@flow-studio/services';

export function WorkflowCanvas() {
    const { nodes, edges, moveNode, addEdge, removeNode, setViewport } =
        useWorkflowStore(state => ({
            nodes     : state.nodes,
            edges     : state.edges,
            moveNode  : state.moveNode,
            addEdge   : state.addEdge,
            removeNode: state.removeNode,
            setViewport: state.setViewport
        }), shallow);

    return (
        <ReactFlow
            nodes={nodes}
            edges={edges}
            onNodeDragStop={(_, node) => moveNode(node.id, node.position)}
            onConnect={connection => addEdge({
                id: crypto.randomUUID(),
                source: connection.source!, sourceHandle: connection.sourceHandle!,
                target: connection.target!, targetHandle: connection.targetHandle!
            })}
            onNodesDelete={nodes => nodes.forEach(n => removeNode(n.id))}
            onEdgesDelete={edges => edges.forEach(e => removeEdge(e.id))}
            onMoveEnd={(_, viewport) => setViewport(viewport)}
        />
    );
}

Why workflowStore is the Master

React Flow has its own internal state. If we let React Flow be the source of truth, the store would need to read from React Flow — creating an inverted dependency. Instead:

Controlled vs. Uncontrolled React Flow

ModeSource of TruthUsed in Flow Studio?
UncontrolledReact Flow internal stateNo
ControlledExternal state (workflowStore)Yes
Do not mix controlled and uncontrolled patterns. If you pass nodes as a prop (controlled) but also call React Flow's internal setNodes directly, you will get state divergence — the canvas will show different nodes than the store holds. Always go through workflowStore actions.