Skip to main content
React and Three.js have fundamentally different rendering models:
  • React: Declarative, re-renders on state change
  • Three.js: Imperative, mutates objects directly
Keeping them synchronized requires careful architecture.

Two Sources of Truth

State LocationPurposeWhat It Controls
React Context (AppContext)UI stateWhat components render, form values, UI toggles
GraphStateManagerRendering stateWhat Three.js displays, node positions, visibility
The useGraph3D hook is the bridge that synchronizes these two state sources.

State Flow

1

User Action

User clicks node, changes depth, toggles filter
2

Dual Update

AppContext dispatch() updates React state; GraphService method updates rendering state
3

useGraph3D Sync

Hook detects React state change and syncs to GraphStateManager
4

Render

React components re-render; Three.js updates canvas
Common Bug Pattern: Updating React state without updating GraphStateManager (or vice versa) causes UI/render mismatch.

AppContext (React State)

Manages all UI-related state using useReducer pattern:
State PropertyTypePurpose
selectedNodeIdsSet<string>Currently selected node IDs
selectedNodeobjectPrimary selected node (for detail panel)
selectionDepthnumberBFS depth expansion (1-5)
selectionModestring’new’, ‘add’, ‘subtract’, ‘intersect’
nodeTypeFilter.hiddenTypesSet<string>Node types to hide
edgeTypeFilter.hiddenTypesSet<string>Edge types to hide
showClassLabelsbooleanShow labels on class nodes
canUndoSelectionbooleanUndo available flag
canRedoSelectionbooleanRedo available flag

GraphStateManager (Rendering State)

Centralized mutable state for Three.js rendering:
State PropertyTypePurpose
graphInstanceForceGraph3DThree.js graph instance
allNodesarrayComplete node dataset
allLinksarrayComplete edge dataset
selectedNodeIdsSet<string>Multi-select node IDs
selectionDepthnumberBFS depth for filtering
instancedRendererInstancedNodeRendererGPU instanced rendering

Synchronization Pattern

When state affects both UI and rendering:

Correct

  1. Update React Context (dispatch)
  2. useEffect in useGraph3D detects change
  3. useEffect syncs to GraphStateManager
  4. useEffect calls service method

Wrong

  • Only updating React Context (UI updates, render doesn’t)
  • Only updating GraphStateManager (render updates, UI doesn’t)
  • Calling service method directly without context update

Critical useEffects in useGraph3D

DependencyActionPurpose
selectedNodeIds, selectionDepth, edgeTypeFilterapplyUnifiedFilter()Update visibility
nodeTypeFilterupdateTypeFilter()Hide/show node types
nodes, edgesupdateGraphData()Load new data
showClassLabels, labelSizeUpdate label settingsVisual changes