React Flow as the Single Source of Truth: How nodeterm Manages Node State
Yes, React Flow serves as the exclusive, live source of truth for all canvas node state in nodeterm, with all persistence layers and auxiliary services reading from or writing to the React Flow array rather than maintaining parallel copies.
In the nodeterm codebase, architectural decisions explicitly designate React Flow as the definitive in-memory model for node positions, sizes, and relationships. Unlike applications that synchronize state between a global store and a rendering layer, nodeterm treats the React Flow component itself as the authoritative data layer, ensuring consistency across peer sync, UI interactions, and disk persistence.
How nodeterm Establishes React Flow as the Authoritative State
The application architecture centralizes all live node representation within the React Flow component. According to the source code in src/shared/canvas-publish.ts, the renderer’s canvas maintains nodes in a React Flow array described explicitly as "the single live source of truth"【src/shared/canvas-publish.ts†L3-L10】.
State Initialization and Hydration
When a project activates, the system does not maintain dual state copies. Instead, persisted node lists undergo conversion directly into live React Flow nodes. In src/renderer/state/workspace.ts, the loading logic ensures parent nodes appear before children to satisfy React Flow’s rendering requirements【src/renderer/state/workspace.ts†L1622-L1624】.
This conversion process means the React Flow array becomes the immediate, mutable representation of the canvas. No intermediate global store holds a parallel copy; the useNodesState hook manages the definitive collection.
Mutation Flow and Direct Updates
All mutations—whether triggered by user actions, peer synchronization payloads, or background services—apply directly to the live React Flow node array. Code at lines 1776-1789 of src/renderer/state/workspace.ts demonstrates that peer mutations transform the existing React Flow nodes in place rather than updating a separate state layer【src/renderer/state/workspace.ts†L1776-L1789】.
Persistence Guards and Data Integrity
To prevent divergent state from reaching disk, nodeterm implements validation logic that confirms the React Flow instance holds the authoritative nodes before committing data.
Validating State Before Persistence
The persist-guard logic in src/renderer/state/persistGuards.ts checks whether the current React Flow instance contains the nodes for the active project. This guard ensures that only the live React Flow state serializes to storage【src/renderer/state/persistGuards.ts†L6-L14】.
Auxiliary data structures, such as the context-link map, derive their content from the live React Flow edges rather than maintaining independent edge state. The context-link map in src/shared/context-link-map.ts builds its relationships by reading the active edge array directly from React Flow【src/shared/context-link-map.ts†L4-L8】.
Practical Implementation: Working with the Single Source
The following patterns demonstrate how nodeterm components interact with the authoritative React Flow state:
// Initialise the live node state – React Flow is the source of truth
const [nodes, setNodes] = useNodesState([]);
// Convert persisted project data into live React Flow nodes
function loadProject(project) {
const flowNodes = nodeStatesToFlow(project.nodeStates); // respects parent-first order
setNodes(flowNodes);
}
// Apply a peer mutation directly to the live React Flow array
function applyPeerMutation(mutation) {
const newNodes = applyMutationToFlow(nodes, mutation);
setNodes(newNodes); // mutation updates the single source
}
// Persist the current canvas only if it matches the active project
if (activeLiveNodeIds.has(node.id)) {
saveProject({ nodeStates: flowToNodeStates(nodes) });
}
This implementation ensures that nodes remains the single source of truth throughout the application lifecycle, from initial hydration through real-time collaboration to final persistence.
Summary
- React Flow owns the live state: The canvas array in React Flow constitutes the exclusive in-memory representation of node positions and relationships, as declared in
src/shared/canvas-publish.ts. - Direct mutation pattern: All updates—UI actions, peer sync, or programmatic changes—apply immediately to the React Flow node array via
setNodes, with no parallel state copies in auxiliary stores. - Parent-first hydration: Loading projects converts persisted data directly into React Flow nodes, ensuring proper rendering order by placing parent nodes before children during initialization.
- Guarded persistence: The system validates that React Flow contains the active project nodes before writing to disk, preventing stale or divergent state from reaching storage.
- Derived auxiliary data: Maps and link structures build themselves from the live React Flow edges rather than maintaining separate edge state, ensuring consistency across the application.
Frequently Asked Questions
Does nodeterm use a global state manager like Redux for node data?
No. According to the source code analysis, nodeterm explicitly avoids maintaining parallel copies of node state in external stores. The React Flow component managed through useNodesState serves as the sole state container, with persistence guards in src/renderer/state/persistGuards.ts ensuring disk writes originate exclusively from the live React Flow array.
How does nodeterm handle real-time collaboration without conflicting state sources?
Peer mutations apply directly to the live React Flow node array in src/renderer/state/workspace.ts (lines 1776-1789). When a sync payload arrives, the system transforms the current React Flow nodes immediately rather than merging with a separate state layer, ensuring all collaborators see the same single source of truth.
What prevents nodeterm from saving stale node data to disk?
The persist-guard logic validates that the current React Flow instance holds the nodes for the active project before committing to storage. As implemented in src/renderer/state/persistGuards.ts (lines 6-14), this check guarantees that only the live, authoritative React Flow state serializes to the persisted project file.
Are edge relationships also managed by React Flow?
Yes. Auxiliary structures like the context-link map derive their data from the live React Flow edges. The code in src/shared/context-link-map.ts (lines 4-8) demonstrates that edge relationships are read directly from the React Flow array, not from a separate edge store, maintaining the single source of truth pattern for connections as well as nodes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →