---
phase: 29-context-optimization
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - jam-ui/src/hooks/useMixerHelper.js
  - 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
autonomous: true

must_haves:
  truths:
    - "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"
    - "useMixerHelper returns stable function references"
  artifacts:
    - path: "jam-ui/src/hooks/useMixerHelper.js"
      provides: "Stable function references via useCallback"
      contains: "useCallback"
    - path: "jam-ui/src/context/MixersContext.js"
      provides: "Memoized context provider value"
      contains: "useMemo"
    - path: "jam-ui/src/context/VuContext.js"
      provides: "Memoized context provider value"
      contains: "useMemo"
    - path: "jam-ui/src/context/GlobalContext.js"
      provides: "Memoized context provider value"
      contains: "useMemo"
    - path: "jam-ui/src/components/client/SessionTrackGain.js"
      provides: "Memoized context consumer"
      contains: "memo"
  key_links:
    - from: "useMixerHelper.js"
      to: "MixersContext.js"
      via: "stable getMixer function"
      pattern: "const getMixer = useCallback"
    - from: "MixersContext.Provider"
      to: "useMixerHelper"
      via: "useMemo wrapper"
      pattern: "useMemo\\(\\(\\) => mixerHelper"
    - from: "SessionTrackGain"
      to: "MixersContext"
      via: "memo-wrapped consumer"
      pattern: "memo\\(function SessionTrackGain"
---

<objective>
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.
</objective>

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

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/29-context-optimization/29-RESEARCH.md
</context>

<tasks>

<task type="auto">
  <name>Task 1a: Stabilize useMixerHelper function references</name>
  <files>
    jam-ui/src/hooks/useMixerHelper.js
  </files>
  <action>
Wrap internal functions in useMixerHelper.js with useCallback to ensure stable references. Without this, memoizing MixersContext.Provider value is ineffective because the mixerHelper object changes every render.

**CRITICAL: The `getMixer` function (line 128) is NOT wrapped with useCallback. This causes ALL dependent functions to recreate every render.**

1. Wrap `getMixer` with useCallback:
   ```javascript
   const getMixer = useCallback((mixerId, mode) => {
     // Only default to mixMode if mode is undefined, not if it's explicitly false
     if (mode === undefined) {
       mode = mixMode;
     }
     return allMixersRef.current[(mode ? 'M' : 'P') + mixerId];
   }, [mixMode]);
   ```
   Note: Uses `allMixersRef.current` (ref, not dependency) and `mixMode` (selector value).

2. Verify these functions are already wrapped (they should be - just confirm):
   - `fillTrackVolumeObject` (line 136) - uses useCallback, depends on getMixer
   - `setMixerVolume` (line 176) - uses useCallback
   - `mediaMixers` (line 216) - uses useCallback
   - `updateMixerData` (line 235) - uses useCallback
   - `getMixerByTrackId` (line 353) - uses useCallback
   - `groupedMixersForClientId` (line 369) - uses useCallback
   - `findMixerForTrack` (line 397) - uses useCallback
   - `mixersForGroupId` (line 592) - uses useCallback
   - `getGroupMixer` (line 608) - uses useCallback
   - `getMixerByResourceId` (line 705) - uses useCallback
   - `mute` (line 721) - uses useCallback, depends on getMixer
   - `faderChanged` (line 751) - uses useCallback, depends on getMixer
   - `panChanged` (line 811) - uses useCallback, depends on getMixer
   - `loopChanged` (line 835) - uses useCallback, depends on getMixer
   - `updateVU` (line 896) - uses useCallback, depends on getMixer
   - `refreshMixer` (line 928) - uses useCallback, depends on getMixer

3. After wrapping getMixer, these dependent functions will automatically stabilize because their `getMixer` dependency will now be stable.

**Why this matters:** Without stable `getMixer`, the dependency chain causes 10+ functions to recreate every render, defeating context memoization.
  </action>
  <verify>
1. Run `grep -n "const getMixer = useCallback" jam-ui/src/hooks/useMixerHelper.js` - should find the wrapped function
2. Run `cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build` - should compile without errors
  </verify>
  <done>
- getMixer is wrapped with useCallback
- All dependent functions now have stable references
- Build passes without errors
  </done>
</task>

<task type="auto">
  <name>Task 1b: Memoize MixersContext provider value</name>
  <files>
    jam-ui/src/context/MixersContext.js
  </files>
  <action>
Memoize the MixersContext.Provider value to prevent new object creation on every render.

**PREREQUISITE:** Task 1a must be complete - useMixerHelper must return stable references for this memoization to be effective.

1. Import `useMemo` from 'react'
2. Wrap the `mixerHelper` value with `useMemo`:
   ```javascript
   const value = useMemo(() => mixerHelper, [mixerHelper]);
   ```
3. Pass `value` to `MixersContext.Provider` instead of `mixerHelper` directly

Now that useMixerHelper returns stable function references (after Task 1a), the memoized context value will only change when actual data changes (allMixers, mixMode, etc.), not on every render.
  </action>
  <verify>
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`
  </verify>
  <done>
- MixersContext.Provider value is wrapped with useMemo
- Build passes without errors
  </done>
</task>

<task type="auto">
  <name>Task 2: Memoize VuContext and GlobalContext provider values</name>
  <files>
    jam-ui/src/context/VuContext.js
    jam-ui/src/context/GlobalContext.js
  </files>
  <action>
Apply the same memoization pattern to VuContext and GlobalContext.

**VuContext.js:**
1. Import `useMemo` from 'react'
2. Memoize the combined value object:
   ```javascript
   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:
   ```javascript
   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
  </action>
  <verify>
1. Run `cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build` - should compile without errors
2. Check VuContext.js contains: `useMemo(() => ({`
3. Check GlobalContext.js contains: `useMemo(() => ({`
  </verify>
  <done>
- VuContext.Provider value is wrapped with useMemo
- GlobalContext.Provider value is wrapped with useMemo
- Build passes without errors
  </done>
</task>

<task type="auto">
  <name>Task 3: Wrap context consumers with React.memo</name>
  <files>
    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
  </files>
  <action>
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:
   ```javascript
   const SessionTrackGain = memo(function SessionTrackGain({ mixers, gainType, ... }) {
     // existing implementation
   });
   ```
3. Add displayName for debugging (optional but helpful):
   ```javascript
   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 = {...}`
  </action>
  <verify>
1. Run `cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build` - should compile without errors
2. Grep for memo usage: `grep -l "memo(function" jam-ui/src/components/client/SessionTrackGain.js jam-ui/src/components/client/JKSessionMyTrack.js`
  </verify>
  <done>
- All 6 consumer components wrapped with React.memo
- Build passes without errors
- Components have displayName for debugging
  </done>
</task>

</tasks>

<verification>
After all tasks complete:

1. **Build verification:**
   ```bash
   cd /Users/nuwan/Code/jam-cloud/jam-ui && npm run build
   ```
   Should complete without errors or warnings related to memo/useMemo.

2. **Code verification:**
   ```bash
   # Check useMixerHelper getMixer stabilization
   grep -n "const getMixer = useCallback" jam-ui/src/hooks/useMixerHelper.js

   # 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)
</verification>

<success_criteria>
- CTX-00: useMixerHelper.getMixer is wrapped with useCallback (prerequisite for CTX-01)
- 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>

<output>
After completion, create `.planning/phases/29-context-optimization/29-01-SUMMARY.md` using the summary template.
</output>
