fix(29): revise plan based on checker feedback

- Split Task 1 into Task 1a (stabilize useMixerHelper) and Task 1b (memoize MixersContext)
- Add useMixerHelper.js to files_modified
- Add getMixer useCallback wrapping as prerequisite for context memoization
- Add key_link verifying useMixerHelper -> MixersContext wiring
- Add CTX-00 success criterion for getMixer stabilization

Issues addressed:
- task_completeness: Task 1 now includes getMixer useCallback wrapping
- key_links_planned: Added verification for useMixerHelper stabilization
This commit is contained in:
Nuwan 2026-03-05 17:22:30 +05:30
parent c595d5334e
commit 468860d54f
1 changed files with 72 additions and 4 deletions

View File

@ -5,6 +5,7 @@ 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
@ -22,7 +23,11 @@ must_haves:
- "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"
@ -36,6 +41,10 @@ must_haves:
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"
@ -69,13 +78,70 @@ Output: Memoized context providers (MixersContext, VuContext, GlobalContext) and
<tasks>
<task type="auto">
<name>Task 1: Memoize MixersContext provider value</name>
<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
@ -83,9 +149,7 @@ Memoize the MixersContext.Provider value to prevent new object creation on every
```
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).
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
@ -222,6 +286,9 @@ After all tasks complete:
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
@ -243,6 +310,7 @@ After all tasks complete:
</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)