Skip to content

Hook Instrumentation

lpetronika edited this page Feb 16, 2026 · 2 revisions

Hook Instrumentation

The src/hooks/ directory is the interface between the React runtime and the Basis engine. Every standard React hook is wrapped in a proxy that injects telemetry while keeping 100% API parity with the original React hooks.

7.1 The Proxy Pattern

Each Basis hook works as a wrapper around the real React hook. It calls the standard hook internally, but extends the signature to accept an optional label (injected at compile-time by the Babel plugin).

// Internal Logic Flow
1. Receive call from User/Babel.
2. Initialize standard React hook.
3. Register the hook's Role (LOCAL, CONTEXT, or PROJ) with the Engine.
4. Intercept the dispatcher (setter) to record temporal pulses.
5. Return standard React values to the component.

7.2 Stateful Interception (useState, useReducer)

Normally, React state setters are black boxes. Basis wraps the dispatcher in a useCallback that notifies the engine before the actual state change happens.

  • Registration: On mount, the hook calls registerVariable(label, { role: SignalRole.LOCAL }).
  • Interception: The setter is proxied. When called, it runs recordUpdate(label) first.
  • Circuit Breaking: If the engine detects an infinite loop (>150 updates/sec), recordUpdate returns false and the hook blocks the state update entirely. This saves the browser thread from freezing.

7.3 Context Instrumentation (createContext, useContext)

v0.5.0 added instrumentation for global context to enable Subspace Decomposition.

  • createContext: The proxy attaches a hidden, non-enumerable _basis_label to the context object.
  • useContext: When a component consumes a context, the proxy uses a useLayoutEffect to pulse that context's label. This lets the engine see the update rhythm of the context and identify local hooks that are shadowing it.

7.4 Causal Bracketing (useEffect, useLayoutEffect)

To build the causal graph, the engine needs to know which effect caused which state update. This is done with a "bracketing" technique:

export function useEffect(effect: React.EffectCallback, deps?: React.DependencyList, label?: string) {
  const effectiveLabel = label || 'anonymous_effect';
  reactUseEffect(() => {
    beginEffectTracking(effectiveLabel); // Set global source
    const destructor = effect();         // Execute user logic
    endEffectTracking();                // Clear global source
    return destructor;
  }, deps);
}

Any state update that happens while an effect is "bracketed" gets an edge in the graph pointing from that effect. This is how the engine detects Double Render Cycles.

7.5 Signal Roles

Not all signals are equal. The instrumentation layer classifies hooks into three roles:

  1. Subspace Anchors ($\Omega$): Global Contexts. Treated as the source of truth.
  2. Basis Vectors (!): Local hooks (useState). Audited for independence from the anchors.
  3. Projections: Derived signals (useMemo, useCallback). The engine checks if these are stable. If a projection pulses on every tick, it gets flagged as unstable, which usually means a missing dependency or reference drift.

7.6 React 19 Support

Basis instruments the React 19 Actions API. Optimistic updates and action states are treated as regular signals in the system.

  • useOptimistic: Tracked as a local signal, same as useState.
  • useActionState: The engine correlates the pending transition with the primary state mutation, so async actions don't create false edges in the graph.

7.7 Lifecycle Cleanup

Every registered variable is cleaned up when its component unmounts:

return () => unregisterVariable(effectiveLabel);

This keeps the system clean. The efficiency score and rankings only reflect currently active components. No zombie signals from unmounted components leaking into the audit.

Clone this wiki locally