jam-cloud/.planning/phases/29-context-optimization/29-01-PLAN.md

9.2 KiB

phase plan type wave depends_on files_modified autonomous must_haves
29-context-optimization 01 execute 1
jam-ui/src/context/MixersContext.js
jam-ui/src/context/VuContext.js
jam-ui/src/context/GlobalContext.js
jam-ui/src/components/client/SessionTrackGain.js
jam-ui/src/components/client/JKSessionMyTrack.js
jam-ui/src/components/client/JKSessionVolumeModal.js
jam-ui/src/components/client/JKSessionPanModal.js
jam-ui/src/components/client/JKSessionBackingTrack.js
jam-ui/src/components/client/JKSessionMetronome.js
true
truths artifacts key_links
Volume slider change does not re-render VU meters
VU update does not re-render volume sliders
MixersContext.Provider value is memoized
Context consumers only re-render when their specific data changes
path provides contains
jam-ui/src/context/MixersContext.js Memoized context provider value useMemo
path provides contains
jam-ui/src/context/VuContext.js Memoized context provider value useMemo
path provides contains
jam-ui/src/context/GlobalContext.js Memoized context provider value useMemo
path provides contains
jam-ui/src/components/client/SessionTrackGain.js Memoized context consumer memo
from to via pattern
MixersContext.Provider useMixerHelper useMemo wrapper useMemo(() => mixerHelper
from to via pattern
SessionTrackGain MixersContext memo-wrapped consumer memo(function SessionTrackGain
Optimize React context providers and consumers to prevent unnecessary re-renders when context values change.

Purpose: MixersContext creates a new value object on every render (line 10), causing all consumers to re-render even when underlying data hasn't changed. VuContext and GlobalContext have the same issue. This phase applies useMemo to provider values and React.memo to consumer components, so volume slider changes don't re-render VU meters and vice versa.

Output: Memoized context providers (MixersContext, VuContext, GlobalContext) and memo-wrapped consumer components (SessionTrackGain, JKSessionMyTrack, etc.) with verified stable function references.

<execution_context> @/Users/nuwan/.claude/get-shit-done/workflows/execute-plan.md @/Users/nuwan/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/29-context-optimization/29-RESEARCH.md Task 1: Memoize MixersContext provider value jam-ui/src/context/MixersContext.js Memoize the MixersContext.Provider value to prevent new object creation on every render.
  1. Import useMemo from 'react'
  2. Wrap the mixerHelper value with useMemo:
    const value = useMemo(() => mixerHelper, [mixerHelper]);
    
  3. Pass value to MixersContext.Provider instead of mixerHelper directly

The useMixerHelper hook already returns stable references (verified: all functions use useCallback, myTracks uses useMemo). The memoization ensures the context value object itself doesn't recreate unless mixerHelper changes.

Do NOT change useMixerHelper.js - the returned object from useMixerHelper already has stable function references via useCallback (faderChanged, findMixerForTrack, updateMixerData, etc.) and memoized values (myTracks via useMemo).

  1. Run cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build - should compile without errors
  2. Check MixersContext.js contains: import { useMemo } and useMemo(() => mixerHelper
  • MixersContext.Provider value is wrapped with useMemo
  • Build passes without errors
Task 2: Memoize VuContext and GlobalContext provider values jam-ui/src/context/VuContext.js jam-ui/src/context/GlobalContext.js Apply the same memoization pattern to VuContext and GlobalContext.

VuContext.js:

  1. Import useMemo from 'react'
  2. Memoize the combined value object:
    const value = useMemo(() => ({
      ...vuHelpers,
      vuStore,
    }), [vuHelpers]);
    
    Note: vuStore is a stable module-level reference, doesn't need to be a dependency

GlobalContext.js:

  1. Import useMemo from 'react'

  2. Memoize the provider value using all state/callback dependencies:

    const value = useMemo(() => ({
      trackVolumeObject,
      setTrackVolumeObject,
      globalObject,
      setGlobalObject,
      metronomeState,
      updateMetronomeState,
      openMetronome,
      closeMetronome,
      resetMetronome,
    }), [
      trackVolumeObject,
      globalObject,
      metronomeState,
      updateMetronomeState,
      openMetronome,
      closeMetronome,
      resetMetronome
    ]);
    

    Note: useState setters (setTrackVolumeObject, setGlobalObject) are stable and don't need to be dependencies

  3. Pass memoized value to provider instead of inline object

  4. Run cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build - should compile without errors

  5. Check VuContext.js contains: useMemo(() => ({

  6. Check GlobalContext.js contains: useMemo(() => ({

  • VuContext.Provider value is wrapped with useMemo
  • GlobalContext.Provider value is wrapped with useMemo
  • Build passes without errors
Task 3: Wrap context consumers with React.memo jam-ui/src/components/client/SessionTrackGain.js jam-ui/src/components/client/JKSessionMyTrack.js jam-ui/src/components/client/JKSessionVolumeModal.js jam-ui/src/components/client/JKSessionPanModal.js jam-ui/src/components/client/JKSessionBackingTrack.js jam-ui/src/components/client/JKSessionMetronome.js Wrap each context consumer component with React.memo to prevent re-renders when props haven't changed.

Pattern for each file:

  1. Import memo from 'react' (add to existing import)
  2. Wrap the component function definition with memo:
    const SessionTrackGain = memo(function SessionTrackGain({ mixers, gainType, ... }) {
      // existing implementation
    });
    
  3. Add displayName for debugging (optional but helpful):
    SessionTrackGain.displayName = 'SessionTrackGain';
    

Files to update:

  • SessionTrackGain.js - volume slider, consumes MixersContext
  • JKSessionMyTrack.js - local track display, consumes MixersContext
  • JKSessionVolumeModal.js - volume popup, consumes MixersContext
  • JKSessionPanModal.js - pan control popup, consumes MixersContext
  • JKSessionBackingTrack.js - backing track display, consumes MixersContext
  • JKSessionMetronome.js - metronome controls, consumes MixersContext

Do NOT wrap JKSessionScreen.js (the top-level container) - only wrap the specific consumer components that are frequently re-rendered.

For each component:

  1. Check if already wrapped with memo - skip if so
  2. Convert const ComponentName = (props) => { to const ComponentName = memo(function ComponentName(props) {
  3. Ensure PropTypes remain attached: ComponentName.propTypes = {...}
  4. Run cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build - should compile without errors
  5. Grep for memo usage: grep -l "memo(function" jam-ui/src/components/client/SessionTrackGain.js jam-ui/src/components/client/JKSessionMyTrack.js
  • All 6 consumer components wrapped with React.memo
  • Build passes without errors
  • Components have displayName for debugging
After all tasks complete:
  1. Build verification:

    cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build
    

    Should complete without errors or warnings related to memo/useMemo.

  2. Code verification:

    # Check MixersContext memoization
    grep -n "useMemo" jam-ui/src/context/MixersContext.js
    
    # Check VuContext memoization
    grep -n "useMemo" jam-ui/src/context/VuContext.js
    
    # Check GlobalContext memoization
    grep -n "useMemo" jam-ui/src/context/GlobalContext.js
    
    # Check memo wrappers
    grep -l "memo(function" jam-ui/src/components/client/*.js
    
  3. Functional verification (manual):

    • Open session in browser with React DevTools Profiler
    • Move volume slider
    • Verify VU meters do NOT show in Profiler re-render list
    • Verify only SessionTrackGain re-renders (due to local state)

<success_criteria>

  • CTX-01: MixersContext.Provider value is memoized (useMemo wraps mixerHelper)
  • CTX-02: VuContext separated from MixerConfigContext (already done in Phase 28, now memoized)
  • CTX-03: Context consumers only subscribe to data they actually use (React.memo prevents prop-unchanged re-renders)
  • Volume slider change doesn't re-render VU meters
  • VU update doesn't re-render volume sliders
  • All 3 context providers use useMemo
  • All 6 consumer components wrapped with React.memo
  • Build passes without errors </success_criteria>
After completion, create `.planning/phases/29-context-optimization/29-01-SUMMARY.md` using the summary template.