docs(29): create phase plan

Phase 29: Context Optimization
- 1 plan in 1 wave
- Memoize MixersContext, VuContext, GlobalContext providers
- Wrap 6 consumer components with React.memo
- Ready for execution
This commit is contained in:
Nuwan 2026-03-05 17:18:20 +05:30
parent ceec2342fe
commit c595d5334e
2 changed files with 261 additions and 3 deletions

View File

@ -49,10 +49,10 @@ Plans:
2. VuContext separated from MixerConfigContext
3. Volume slider change doesn't re-render VU meters
4. VU update doesn't re-render volume sliders
**Plans**: TBD
**Plans**: 1 plan
Plans:
- [ ] 29-01: TBD
- [ ] 29-01-PLAN.md — Memoize context providers (MixersContext, VuContext, GlobalContext) and wrap consumers with React.memo
### Phase 30: Component Memoization
**Goal**: Child components don't re-render when parent re-renders with same props
@ -104,7 +104,7 @@ Plans:
| Phase | Milestone | Plans Complete | Status | Completed |
|-------|-----------|----------------|--------|-----------|
| 28. VU Meter Optimization | v1.7 | 2/2 | ✓ Complete | 2026-03-05 |
| 29. Context Optimization | v1.7 | 0/TBD | Not started | - |
| 29. Context Optimization | v1.7 | 0/1 | Planned | - |
| 30. Component Memoization | v1.7 | 0/TBD | Not started | - |
| 31. Selector Optimization | v1.7 | 0/TBD | Not started | - |
| 32. State Update Optimization | v1.7 | 0/TBD | Not started | - |

View File

@ -0,0 +1,258 @@
---
phase: 29-context-optimization
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- 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"
artifacts:
- 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: "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 1: 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.
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
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).
</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 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-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>