245 lines
7.9 KiB
Markdown
245 lines
7.9 KiB
Markdown
---
|
|
phase: 32-state-update-optimization
|
|
plan: 02
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- jam-ui/src/hooks/useMixerHelper.js
|
|
autonomous: true
|
|
|
|
must_haves:
|
|
truths:
|
|
- "Mixer categorization doesn't dispatch when content unchanged"
|
|
- "Redux state only updates when mixer arrays meaningfully change"
|
|
- "Unchanged mixer list doesn't trigger downstream re-renders"
|
|
artifacts:
|
|
- path: "jam-ui/src/hooks/useMixerHelper.js"
|
|
provides: "Content comparison before dispatch"
|
|
contains: "mixerArraysEqual"
|
|
key_links:
|
|
- from: "jam-ui/src/hooks/useMixerHelper.js"
|
|
to: "setMetronomeTrackMixers"
|
|
via: "conditional dispatch"
|
|
pattern: "if.*!mixerArraysEqual.*dispatch"
|
|
---
|
|
|
|
<objective>
|
|
Prevent redundant Redux dispatches in mixer categorization
|
|
|
|
Purpose: Stop dispatching mixer category arrays when their content hasn't changed (STATE-03). Currently, every mixer state change triggers 5 category dispatches even if the categories haven't changed, causing unnecessary re-renders.
|
|
|
|
Output: useMixerHelper with content comparison before each category dispatch
|
|
</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/32-state-update-optimization/32-RESEARCH.md
|
|
|
|
# Source file to modify
|
|
@jam-ui/src/hooks/useMixerHelper.js
|
|
@jam-ui/src/store/features/mixersSlice.js
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Add mixer array comparison helper</name>
|
|
<files>jam-ui/src/hooks/useMixerHelper.js</files>
|
|
<action>
|
|
Add a helper function to compare mixer arrays by their IDs. Place this near the top of the file, after imports but before the hook function.
|
|
|
|
```javascript
|
|
/**
|
|
* Compare two mixer arrays by their IDs to determine if content changed.
|
|
* Used to prevent unnecessary Redux dispatches when categorizing mixers.
|
|
*
|
|
* @param {Array} prev - Previous mixer array
|
|
* @param {Array} next - New mixer array
|
|
* @returns {boolean} True if arrays have same content (by ID)
|
|
*/
|
|
const mixerArraysEqual = (prev, next) => {
|
|
// Same reference = definitely equal
|
|
if (prev === next) return true;
|
|
|
|
// Different lengths = definitely not equal
|
|
if (!prev || !next || prev.length !== next.length) return false;
|
|
|
|
// Compare by mixer pair IDs (master.id + personal.id creates unique pair key)
|
|
const getIds = (arr) => arr
|
|
.map(pair => `${pair.master?.id || ''}-${pair.personal?.id || ''}`)
|
|
.sort()
|
|
.join(',');
|
|
|
|
return getIds(prev) === getIds(next);
|
|
};
|
|
```
|
|
|
|
Why ID-based comparison:
|
|
- Mixer objects have stable `id` fields
|
|
- Deep comparison with JSON.stringify is slow and fragile
|
|
- We only care if the set of mixers changed, not individual properties
|
|
- Master/personal pair creates unique identifier
|
|
</action>
|
|
<verify>
|
|
Helper function exists:
|
|
`grep -A 10 "mixerArraysEqual" jam-ui/src/hooks/useMixerHelper.js | head -15`
|
|
</verify>
|
|
<done>
|
|
mixerArraysEqual helper function added to useMixerHelper.js
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Add selectors for current category state</name>
|
|
<files>jam-ui/src/hooks/useMixerHelper.js</files>
|
|
<action>
|
|
Import the category selectors and add refs to track previous values. Find the existing selector imports and add the category selectors.
|
|
|
|
1. Update imports from mixersSlice (around line 10-30):
|
|
```javascript
|
|
import {
|
|
// ... existing imports ...
|
|
selectMetronomeTrackMixers,
|
|
selectBackingTrackMixers,
|
|
selectJamTrackMixers,
|
|
selectRecordingTrackMixers,
|
|
selectAdhocTrackMixers,
|
|
} from '../store/features/mixersSlice';
|
|
```
|
|
|
|
2. Inside the useMixerHelper function, add useRef to track previous values. Place this near other refs/selectors:
|
|
```javascript
|
|
// Track previous category values for comparison
|
|
const prevCategoriesRef = useRef({
|
|
metronome: [],
|
|
backing: [],
|
|
jam: [],
|
|
recording: [],
|
|
adhoc: []
|
|
});
|
|
```
|
|
|
|
Note: We use a ref instead of selectors to avoid circular updates. The ref stores the last dispatched values so we can compare before dispatching again.
|
|
</action>
|
|
<verify>
|
|
Category selectors imported:
|
|
`grep "selectMetronomeTrackMixers\|selectBackingTrackMixers" jam-ui/src/hooks/useMixerHelper.js | head -5`
|
|
|
|
prevCategoriesRef defined:
|
|
`grep "prevCategoriesRef" jam-ui/src/hooks/useMixerHelper.js | head -3`
|
|
</verify>
|
|
<done>
|
|
Category selectors imported and prevCategoriesRef added
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 3: Add conditional dispatch to categorization useEffect</name>
|
|
<files>jam-ui/src/hooks/useMixerHelper.js</files>
|
|
<action>
|
|
Modify the categorization useEffect (around lines 236-297) to only dispatch when content changes.
|
|
|
|
Find the current dispatch block:
|
|
```javascript
|
|
// Dispatch to Redux
|
|
dispatch(setMetronomeTrackMixers(metronomeTrackMixers));
|
|
dispatch(setBackingTrackMixers(backingTrackMixers));
|
|
dispatch(setJamTrackMixers(jamTrackMixers));
|
|
dispatch(setRecordingTrackMixers(recordingTrackMixers));
|
|
dispatch(setAdhocTrackMixers(adhocTrackMixers));
|
|
```
|
|
|
|
Replace with conditional dispatches:
|
|
```javascript
|
|
// Dispatch to Redux ONLY if content changed
|
|
// This prevents unnecessary re-renders when mixer objects change but categories stay same
|
|
|
|
if (!mixerArraysEqual(prevCategoriesRef.current.metronome, metronomeTrackMixers)) {
|
|
console.log('[useMixerHelper] Metronome mixers changed, dispatching');
|
|
dispatch(setMetronomeTrackMixers(metronomeTrackMixers));
|
|
prevCategoriesRef.current.metronome = metronomeTrackMixers;
|
|
}
|
|
|
|
if (!mixerArraysEqual(prevCategoriesRef.current.backing, backingTrackMixers)) {
|
|
console.log('[useMixerHelper] Backing track mixers changed, dispatching');
|
|
dispatch(setBackingTrackMixers(backingTrackMixers));
|
|
prevCategoriesRef.current.backing = backingTrackMixers;
|
|
}
|
|
|
|
if (!mixerArraysEqual(prevCategoriesRef.current.jam, jamTrackMixers)) {
|
|
console.log('[useMixerHelper] Jam track mixers changed, dispatching');
|
|
dispatch(setJamTrackMixers(jamTrackMixers));
|
|
prevCategoriesRef.current.jam = jamTrackMixers;
|
|
}
|
|
|
|
if (!mixerArraysEqual(prevCategoriesRef.current.recording, recordingTrackMixers)) {
|
|
console.log('[useMixerHelper] Recording mixers changed, dispatching');
|
|
dispatch(setRecordingTrackMixers(recordingTrackMixers));
|
|
prevCategoriesRef.current.recording = recordingTrackMixers;
|
|
}
|
|
|
|
if (!mixerArraysEqual(prevCategoriesRef.current.adhoc, adhocTrackMixers)) {
|
|
console.log('[useMixerHelper] Adhoc mixers changed, dispatching');
|
|
dispatch(setAdhocTrackMixers(adhocTrackMixers));
|
|
prevCategoriesRef.current.adhoc = adhocTrackMixers;
|
|
}
|
|
```
|
|
|
|
Note: Console logs are useful for debugging but can be commented out in production. They help verify that dispatches are being skipped when expected.
|
|
</action>
|
|
<verify>
|
|
Conditional dispatch pattern present:
|
|
`grep -A 3 "mixerArraysEqual.*metronome" jam-ui/src/hooks/useMixerHelper.js | head -5`
|
|
|
|
All 5 categories have conditional checks:
|
|
`grep -c "mixerArraysEqual.*prevCategoriesRef" jam-ui/src/hooks/useMixerHelper.js` should be 5
|
|
|
|
ESLint passes:
|
|
`cd jam-ui && npx eslint src/hooks/useMixerHelper.js --max-warnings=0`
|
|
</verify>
|
|
<done>
|
|
Mixer categorization only dispatches when content changes, tracked via prevCategoriesRef
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
1. Helper function added:
|
|
```bash
|
|
grep -c "mixerArraysEqual" jam-ui/src/hooks/useMixerHelper.js
|
|
```
|
|
Should return at least 6 (1 definition + 5 usages)
|
|
|
|
2. Conditional dispatch pattern:
|
|
```bash
|
|
grep "if (!mixerArraysEqual" jam-ui/src/hooks/useMixerHelper.js | wc -l
|
|
```
|
|
Should return 5
|
|
|
|
3. ESLint clean:
|
|
```bash
|
|
cd jam-ui && npx eslint src/hooks/useMixerHelper.js --max-warnings=0
|
|
```
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- [ ] mixerArraysEqual helper function created
|
|
- [ ] prevCategoriesRef tracks previously dispatched values
|
|
- [ ] All 5 category dispatches use conditional check
|
|
- [ ] prevCategoriesRef updated only when dispatch occurs
|
|
- [ ] ESLint passes with no warnings
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/32-state-update-optimization/32-02-SUMMARY.md`
|
|
</output>
|