docs(29): research context optimization phase domain
Phase 29: Context Optimization - Standard stack identified (useMemo, useCallback, memo built-ins) - Architecture patterns documented (memoized provider values) - Pitfalls catalogued (stale closures, missing dependencies)
This commit is contained in:
parent
393bdfe057
commit
ceec2342fe
|
|
@ -0,0 +1,496 @@
|
|||
# Phase 29: Context Optimization - Research
|
||||
|
||||
**Researched:** 2026-03-05
|
||||
**Domain:** React context performance optimization
|
||||
**Confidence:** HIGH
|
||||
|
||||
## Summary
|
||||
|
||||
Phase 29 addresses unnecessary re-renders caused by unmemoized context provider values in MixersContext and the coupling of high-frequency VU updates with low-frequency mixer configuration updates. Phase 28 successfully extracted VU data into an external store, but MixersContext still creates a new value object on every render (line 10 of MixersContext.js), triggering cascading re-renders in all consumer components even when the underlying data hasn't changed.
|
||||
|
||||
The solution involves three complementary strategies:
|
||||
1. **Memoize context provider values** - Use `useMemo` to prevent new object creation on every render
|
||||
2. **Split contexts by update frequency** - Separate VuContext (already simplified in Phase 28) from MixersContext configuration
|
||||
3. **Optimize context consumers** - Use `React.memo` and selector patterns to minimize re-render scope
|
||||
|
||||
This phase builds directly on Phase 28's work. VuContext was already simplified to provide vuStore reference; now MixersContext needs similar optimization for mixer configuration data (volume, pan, mute state, etc.).
|
||||
|
||||
**Primary recommendation:** Wrap the MixersContext.Provider value with useMemo, using appropriate dependencies from useMixerHelper. Since Phase 28 already separated VU updates via external store, this phase focuses exclusively on preventing re-renders from mixer configuration changes.
|
||||
|
||||
## Standard Stack
|
||||
|
||||
The established libraries/tools for this domain:
|
||||
|
||||
### Core
|
||||
| Library | Version | Purpose | Why Standard |
|
||||
|---------|---------|---------|--------------|
|
||||
| React useMemo | built-in (16.13.1) | Memoize context values | Official React API for preventing object recreation |
|
||||
| React useCallback | built-in (16.13.1) | Memoize function references | Stabilizes functions passed to context consumers |
|
||||
| React memo | built-in (16.13.1) | Prevent component re-renders | Standard pattern for optimizing context consumers |
|
||||
|
||||
### Supporting
|
||||
| Library | Version | Purpose | When to Use |
|
||||
|---------|---------|---------|-------------|
|
||||
| use-sync-external-store | 1.6.0 (already installed) | External store subscription | Already used for vuStore in Phase 28 |
|
||||
| @reduxjs/toolkit | 1.6.1 (already installed) | State management | Already used for mixer state in Redux |
|
||||
|
||||
### Alternatives Considered
|
||||
| Instead of | Could Use | Tradeoff |
|
||||
|------------|-----------|----------|
|
||||
| useMemo | use-context-selector | Added dependency; React 19+ has native support; useMemo simpler for this phase |
|
||||
| Context splitting | Single optimized context | Splitting better separates concerns; VuContext already exists |
|
||||
| React.memo | PureComponent | Functional components are codebase standard |
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
# No new dependencies required
|
||||
# All optimization APIs are React built-ins or already installed
|
||||
```
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Recommended Project Structure
|
||||
```
|
||||
src/
|
||||
├── context/
|
||||
│ ├── MixersContext.js # MODIFY: Add useMemo to provider value
|
||||
│ ├── VuContext.js # ALREADY OPTIMIZED in Phase 28
|
||||
│ └── GlobalContext.js # CHECK: May need useMemo for trackVolumeObject
|
||||
├── hooks/
|
||||
│ ├── useMixerHelper.js # VERIFY: Return stable references
|
||||
│ └── useVuHelpers.js # ALREADY OPTIMIZED in Phase 28
|
||||
└── components/
|
||||
└── client/
|
||||
├── JKSessionMyTrack.js # CONSUMER: Verify memo-wrapped
|
||||
├── SessionTrackGain.js # CONSUMER: Verify memo-wrapped
|
||||
└── JKSessionVolumeModal.js # CONSUMER: Verify memo-wrapped
|
||||
```
|
||||
|
||||
### Pattern 1: Memoized Context Provider Value
|
||||
**What:** Wrap the context provider value with useMemo to prevent object recreation on every render.
|
||||
**When to use:** When context value is an object containing multiple properties or functions.
|
||||
**Example:**
|
||||
```javascript
|
||||
// Source: React official docs + Kent C. Dodds blog
|
||||
// Modify MixersContext.js
|
||||
|
||||
import React, { createContext, useContext, useMemo } from 'react';
|
||||
import useMixerHelper from '../hooks/useMixerHelper.js';
|
||||
|
||||
const MixersContext = createContext();
|
||||
|
||||
export const MixersProvider = ({ children }) => {
|
||||
const mixerHelper = useMixerHelper();
|
||||
|
||||
// Memoize the context value - only recreate if mixerHelper changes
|
||||
const value = useMemo(() => mixerHelper, [mixerHelper]);
|
||||
|
||||
return (
|
||||
<MixersContext.Provider value={value}>
|
||||
{children}
|
||||
</MixersContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useMixersContext = () => {
|
||||
const context = useContext(MixersContext);
|
||||
if (!context) {
|
||||
throw new Error('useMixersContext must be used within a MixersProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export default MixersContext;
|
||||
```
|
||||
|
||||
### Pattern 2: Stable Function References with useCallback
|
||||
**What:** Wrap functions returned from hooks with useCallback to ensure stable references across renders.
|
||||
**When to use:** When functions are part of context value or passed as props to memoized components.
|
||||
**Example:**
|
||||
```javascript
|
||||
// Source: React official docs for useCallback
|
||||
// Pattern to apply in useMixerHelper.js
|
||||
|
||||
const faderChanged = useCallback(async (data, mixers, gainType, controlGroup) => {
|
||||
// Implementation stays the same
|
||||
// ...existing code...
|
||||
}, [getOriginalVolume, getMixer, fillTrackVolumeObject, setMixerVolume, allMixers, trackVolumeObject, dispatch]);
|
||||
|
||||
// CRITICAL: Ensure ALL functions in the return object use useCallback
|
||||
// so that the entire mixerHelper object has stable references
|
||||
```
|
||||
|
||||
### Pattern 3: Context Consumer Memoization
|
||||
**What:** Wrap context consumer components with React.memo to prevent re-renders when props haven't changed.
|
||||
**When to use:** For all components that consume context and render frequently.
|
||||
**Example:**
|
||||
```javascript
|
||||
// Source: React official docs for memo
|
||||
// Pattern for SessionTrackGain.js
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { useMixersContext } from '../../context/MixersContext';
|
||||
|
||||
const SessionTrackGain = memo(function SessionTrackGain({
|
||||
mixers,
|
||||
gainType,
|
||||
controlGroup,
|
||||
sessionController,
|
||||
orientation = 'vertical'
|
||||
}) {
|
||||
const mixerHelper = useMixersContext();
|
||||
// ... component implementation ...
|
||||
});
|
||||
|
||||
export default SessionTrackGain;
|
||||
```
|
||||
|
||||
### Pattern 4: Split Context by Update Frequency
|
||||
**What:** Separate high-frequency updates (VU) from low-frequency updates (mixer config) into different contexts.
|
||||
**When to use:** When context contains data that updates at dramatically different rates.
|
||||
**Example:**
|
||||
```javascript
|
||||
// Source: Kent C. Dodds optimization guide + developerway.com patterns
|
||||
// This pattern is ALREADY IMPLEMENTED in Phase 28
|
||||
|
||||
// VuContext.js - High-frequency updates (50-70/sec) via external store
|
||||
export const VuProvider = ({ children }) => {
|
||||
const vuHelpers = useVuHelpers();
|
||||
|
||||
const value = useMemo(() => ({
|
||||
...vuHelpers,
|
||||
vuStore,
|
||||
}), [vuHelpers]);
|
||||
|
||||
return <VuContext.Provider value={value}>{children}</VuContext.Provider>;
|
||||
};
|
||||
|
||||
// MixersContext.js - Low-frequency updates (user interactions)
|
||||
export const MixersProvider = ({ children }) => {
|
||||
const mixerHelper = useMixerHelper();
|
||||
const value = useMemo(() => mixerHelper, [mixerHelper]);
|
||||
return <MixersContext.Provider value={value}>{children}</MixersContext.Provider>;
|
||||
};
|
||||
```
|
||||
|
||||
### Anti-Patterns to Avoid
|
||||
- **Creating new objects in provider:** `value={{ ...mixerHelper }}` creates new object every render
|
||||
- **Inline object literals:** `value={{ data, functions }}` always creates new reference
|
||||
- **Missing dependencies in useMemo:** Stale closures over mixer state
|
||||
- **Overusing useMemo:** Don't memoize primitive values or simple calculations
|
||||
- **Forgetting useCallback for functions:** Functions recreated on every render break memo
|
||||
- **Not combining memo with useMemo:** Memoizing context value alone doesn't prevent consumer re-renders if props change
|
||||
|
||||
## Don't Hand-Roll
|
||||
|
||||
Problems that look simple but have existing solutions:
|
||||
|
||||
| Problem | Don't Build | Use Instead | Why |
|
||||
|---------|-------------|-------------|-----|
|
||||
| Context value comparison | Custom shallow equality | useMemo with correct dependencies | React team optimized useMemo for this; handles edge cases |
|
||||
| Function reference stability | Manual ref tracking | useCallback hook | Built-in, well-tested, works with concurrent rendering |
|
||||
| Component re-render prevention | Custom shouldComponentUpdate | React.memo | Optimized for functional components, simpler API |
|
||||
| Context selector pattern | Custom subscription system | Use useMemo at consumer level | React 19+ has useContextSelector; for React 16 useMemo simpler than library |
|
||||
|
||||
**Key insight:** React's built-in memoization hooks (useMemo, useCallback, memo) are heavily optimized for concurrent rendering and cover the vast majority of context optimization needs. Adding external libraries like use-context-selector adds complexity and bundle size without significant benefit for this specific use case where context updates are already infrequent (user interactions only).
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Pitfall 1: useMemo Dependencies Not Comprehensive
|
||||
**What goes wrong:** Context value still recreated on every render, defeating memoization
|
||||
**Why it happens:** Missing dependency in useMemo array, or dependency itself recreates every render
|
||||
**How to avoid:**
|
||||
- Use ESLint react-hooks/exhaustive-deps rule
|
||||
- Ensure hook return values are stable (useCallback wraps functions)
|
||||
- Verify dependencies with React DevTools Profiler
|
||||
**Warning signs:** React DevTools shows "MixersProvider" re-rendering every time, even without state changes
|
||||
|
||||
### Pitfall 2: Memoizing Value But Not Function References
|
||||
**What goes wrong:** Context value object appears memoized, but functions inside recreate, breaking memo
|
||||
**Why it happens:** Functions returned from hooks aren't wrapped in useCallback
|
||||
**How to avoid:** Wrap ALL functions in useCallback with correct dependencies
|
||||
**Warning signs:** memo-wrapped consumers still re-render; React DevTools highlights function prop changes
|
||||
|
||||
### Pitfall 3: memo Without Stable Props
|
||||
**What goes wrong:** Component wrapped in memo still re-renders frequently
|
||||
**Why it happens:** Parent passes new object/array/function references as props each render
|
||||
**How to avoid:**
|
||||
- useMemo for object/array props
|
||||
- useCallback for function props
|
||||
- Pass primitive values when possible
|
||||
**Warning signs:** Profiler shows "did not skip render" for memo components
|
||||
|
||||
### Pitfall 4: Overusing useMemo for Everything
|
||||
**What goes wrong:** Code becomes harder to read, minimal performance benefit
|
||||
**Why it happens:** Premature optimization without measuring actual performance impact
|
||||
**How to avoid:**
|
||||
- Only memoize context provider values and expensive computations
|
||||
- Don't memoize primitive values or simple JSX
|
||||
- Profile with React DevTools before adding memoization
|
||||
**Warning signs:** Code complexity increases but Profiler shows no improvement
|
||||
|
||||
### Pitfall 5: Stale Closures from Missing Dependencies
|
||||
**What goes wrong:** Memoized functions reference old state/props, causing bugs
|
||||
**Why it happens:** Dependencies array missing reactive values, creating closure over stale data
|
||||
**How to avoid:**
|
||||
- Include ALL reactive values in dependency array
|
||||
- Use ESLint warnings as guide
|
||||
- Test edge cases where state updates quickly
|
||||
**Warning signs:** Functions behave with old data; intermittent bugs during rapid interactions
|
||||
|
||||
### Pitfall 6: Context Splitting Without Clear Boundaries
|
||||
**What goes wrong:** Confusion about which context provides what data
|
||||
**Why it happens:** Context split arbitrarily without clear domain separation
|
||||
**How to avoid:**
|
||||
- Split by update frequency (VU vs config) or domain (auth vs app state)
|
||||
- Document each context's purpose clearly
|
||||
- Name contexts descriptively (VuContext, MixersContext)
|
||||
**Warning signs:** Consumers import multiple contexts; unclear which context owns which data
|
||||
|
||||
## Code Examples
|
||||
|
||||
Verified patterns from official sources:
|
||||
|
||||
### Complete MixersContext Optimization
|
||||
```javascript
|
||||
// Source: React official docs + codebase pattern
|
||||
// Modify jam-ui/src/context/MixersContext.js
|
||||
|
||||
import React, { createContext, useContext, useMemo } from 'react';
|
||||
import useMixerHelper from '../hooks/useMixerHelper.js';
|
||||
|
||||
const MixersContext = createContext();
|
||||
|
||||
export const MixersProvider = ({ children }) => {
|
||||
const mixerHelper = useMixerHelper();
|
||||
|
||||
// CRITICAL: Memoize the entire value object
|
||||
// Since useMixerHelper returns stable references (via useCallback),
|
||||
// this prevents new object creation on every render
|
||||
const value = useMemo(() => mixerHelper, [mixerHelper]);
|
||||
|
||||
return (
|
||||
<MixersContext.Provider value={value}>
|
||||
{children}
|
||||
</MixersContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useMixersContext = () => {
|
||||
const context = useContext(MixersContext);
|
||||
if (!context) {
|
||||
throw new Error('useMixersContext must be used within a MixersProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export default MixersContext;
|
||||
```
|
||||
|
||||
### Verify useMixerHelper Returns Stable References
|
||||
```javascript
|
||||
// Source: Current useMixerHelper.js pattern + React useCallback docs
|
||||
// Audit jam-ui/src/hooks/useMixerHelper.js return statement
|
||||
|
||||
// BEFORE (if needed):
|
||||
return {
|
||||
faderChanged: (data, mixers) => { /* impl */ }, // ❌ New function every render
|
||||
myTracks, // ❌ May recreate if not memoized
|
||||
mixMode
|
||||
};
|
||||
|
||||
// AFTER:
|
||||
const faderChanged = useCallback(async (data, mixers, gainType, controlGroup) => {
|
||||
// ...existing implementation...
|
||||
}, [getOriginalVolume, getMixer, fillTrackVolumeObject, setMixerVolume, allMixers, trackVolumeObject, dispatch]);
|
||||
|
||||
const myTracks = useMemo(() => {
|
||||
// ...existing computation...
|
||||
}, [currentSession, isConnected, jamClient, allMixers, mixMode, findMixerForTrack, getParticipant, server.clientId]);
|
||||
|
||||
// VERIFIED: All functions use useCallback, computed values use useMemo
|
||||
return {
|
||||
isReady,
|
||||
session: currentSession,
|
||||
mixers: allMixers,
|
||||
myTracks, // ✅ Memoized
|
||||
findMixerForTrack, // ✅ useCallback
|
||||
updateMixerData, // ✅ useCallback
|
||||
updateVU, // ✅ useCallback
|
||||
simulatedMusicCategoryMixers,
|
||||
simulatedChatCategoryMixers,
|
||||
mixMode,
|
||||
faderChanged, // ✅ useCallback
|
||||
initGain, // ✅ useCallback
|
||||
setMixerPan, // ✅ useCallback
|
||||
getAudioInputCategoryMixer, // ✅ useCallback
|
||||
getChatCategoryMixer, // ✅ useCallback
|
||||
backingTracks,
|
||||
jamTracks,
|
||||
recordedTracks
|
||||
};
|
||||
```
|
||||
|
||||
### Memoize Context Consumers
|
||||
```javascript
|
||||
// Source: React memo documentation
|
||||
// Pattern for SessionTrackGain.js and other consumers
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { useMixersContext } from '../../context/MixersContext';
|
||||
|
||||
// Wrap the entire component export with memo
|
||||
const SessionTrackGain = memo(function SessionTrackGain({
|
||||
mixers,
|
||||
gainType,
|
||||
controlGroup,
|
||||
sessionController,
|
||||
orientation = 'vertical'
|
||||
}) {
|
||||
const mixerHelper = useMixersContext();
|
||||
const faderHelpers = useFaderHelpers();
|
||||
|
||||
// Component implementation stays the same
|
||||
// ...
|
||||
|
||||
return (/* JSX */);
|
||||
});
|
||||
|
||||
// Add display name for debugging
|
||||
SessionTrackGain.displayName = 'SessionTrackGain';
|
||||
|
||||
export default SessionTrackGain;
|
||||
```
|
||||
|
||||
### Verify GlobalContext Memoization
|
||||
```javascript
|
||||
// Source: Current GlobalContext.js + memoization pattern
|
||||
// Check jam-ui/src/context/GlobalContext.js
|
||||
|
||||
export const GlobalProvider = ({ children }) => {
|
||||
const [trackVolumeObject, setTrackVolumeObject] = useState({ /* ... */ });
|
||||
const [globalObject, setGlobalObject] = useState({ /* ... */ });
|
||||
const [videoEnabled, setVideoEnabled] = useState(false);
|
||||
|
||||
const { metronomeState, updateMetronomeState, openMetronome, closeMetronome, resetMetronome } = useMetronomeState();
|
||||
|
||||
// CRITICAL: Memoize the provider value
|
||||
const value = useMemo(() => ({
|
||||
trackVolumeObject,
|
||||
setTrackVolumeObject,
|
||||
globalObject,
|
||||
setGlobalObject,
|
||||
metronomeState,
|
||||
updateMetronomeState,
|
||||
openMetronome,
|
||||
closeMetronome,
|
||||
resetMetronome,
|
||||
}), [trackVolumeObject, globalObject, metronomeState, updateMetronomeState, openMetronome, closeMetronome, resetMetronome]);
|
||||
|
||||
return (
|
||||
<GlobalContext.Provider value={value}>
|
||||
{children}
|
||||
</GlobalContext.Provider>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Testing Context Optimization
|
||||
```javascript
|
||||
// Source: React DevTools Profiler best practices
|
||||
// Pattern for verification after implementing optimization
|
||||
|
||||
// Use React DevTools Profiler to verify:
|
||||
// 1. Record a session with Profiler
|
||||
// 2. Move a volume slider (triggers MixersContext update)
|
||||
// 3. Check which components re-rendered
|
||||
|
||||
// BEFORE optimization:
|
||||
// - MixersProvider re-renders ❌
|
||||
// - ALL consumers re-render (JKSessionMyTrack, SessionTrackGain, etc.) ❌
|
||||
// - VU meters re-render ❌
|
||||
|
||||
// AFTER optimization:
|
||||
// - MixersProvider does NOT re-render ✅
|
||||
// - Only consumers with changed props re-render ✅
|
||||
// - VU meters do NOT re-render (external store) ✅
|
||||
|
||||
// Success criteria from requirements:
|
||||
// - CTX-01: MixersContext.Provider value is memoized ✅
|
||||
// - CTX-02: VuContext separated from MixersContext ✅ (Phase 28)
|
||||
// - CTX-03: Volume slider change doesn't re-render VU meters ✅
|
||||
// - Success: VU update doesn't re-render volume sliders ✅
|
||||
```
|
||||
|
||||
## State of the Art
|
||||
|
||||
| Old Approach | Current Approach | When Changed | Impact |
|
||||
|--------------|------------------|--------------|--------|
|
||||
| Inline context value object | useMemo-wrapped value | React 16.8 (2019) | Prevents object recreation on every render |
|
||||
| Class components with shouldComponentUpdate | React.memo for functional components | React 16.6 (2018) | Simpler API, better with hooks |
|
||||
| Single context for all data | Context splitting by domain/frequency | Best practice (2020+) | Reduces re-render scope |
|
||||
| Props drilling | Context API | React 16.3 (2018) | Cleaner component tree, but needs optimization |
|
||||
| use-context-selector library | Native useContextSelector | React 19 (2024) | Built-in selector pattern, but not yet available in React 16 |
|
||||
|
||||
**Deprecated/outdated:**
|
||||
- **PureComponent for context consumers:** Functional components with memo are now standard
|
||||
- **Context.Consumer render prop:** useContext hook is cleaner, more readable
|
||||
- **Separate state/dispatch contexts for every context:** Only split when update frequency differs significantly
|
||||
|
||||
## Open Questions
|
||||
|
||||
Things that couldn't be fully resolved:
|
||||
|
||||
1. **Exact dependencies for MixersContext useMemo**
|
||||
- What we know: Value should be memoized, dependencies should include mixerHelper
|
||||
- What's unclear: Whether useMixerHelper return value is already stable (needs code audit)
|
||||
- Recommendation: Audit useMixerHelper to ensure all functions use useCallback; if stable, dependency is just [mixerHelper]
|
||||
|
||||
2. **Impact of memoizing GlobalContext**
|
||||
- What we know: GlobalContext also provides trackVolumeObject which updates frequently
|
||||
- What's unclear: Whether GlobalContext needs same optimization treatment
|
||||
- Recommendation: Check if GlobalContext consumers experience performance issues; if yes, apply same pattern
|
||||
|
||||
3. **Optimal granularity for context splitting**
|
||||
- What we know: VuContext already separated (Phase 28); MixersContext contains config
|
||||
- What's unclear: Whether MixersContext should be further split (e.g., MixerConfigContext vs MixerActionsContext)
|
||||
- Recommendation: Start with single memoized MixersContext; split further only if profiling shows issues
|
||||
|
||||
4. **React DevTools Profiler verification methodology**
|
||||
- What we know: Need to verify no re-renders from context updates
|
||||
- What's unclear: Exact profiler setup and metrics to capture
|
||||
- Recommendation: Document profiler verification steps in verification task; include before/after screenshots
|
||||
|
||||
## Sources
|
||||
|
||||
### Primary (HIGH confidence)
|
||||
- [React useMemo official docs](https://react.dev/reference/react/useMemo) - API, dependencies, best practices
|
||||
- [React useCallback official docs](https://react.dev/reference/react/useCallback) - Stabilizing function references
|
||||
- [React memo official docs](https://react.dev/reference/react/memo) - Component memoization, context caveats
|
||||
- [Kent C. Dodds - How to optimize your context value](https://kentcdodds.com/blog/how-to-optimize-your-context-value) - Authoritative pattern guide
|
||||
|
||||
### Secondary (MEDIUM confidence)
|
||||
- [How to Handle React Context Performance Issues (2026)](https://oneuptime.com/blog/post/2026-01-24-react-context-performance-issues/view) - Modern context optimization patterns
|
||||
- [developerway.com - React re-renders guide](https://www.developerway.com/posts/react-re-renders-guide) - Comprehensive re-render patterns
|
||||
- [developerway.com - Performant React apps with Context](https://www.developerway.com/posts/how-to-write-performant-react-apps-with-context) - Context performance strategies
|
||||
- [Josh Comeau - useMemo and useCallback](https://www.joshwcomeau.com/react/usememo-and-usecallback/) - Clear explanations with examples
|
||||
|
||||
### Tertiary (LOW confidence)
|
||||
- [use-context-selector npm](https://www.npmjs.com/package/use-context-selector) - Selector pattern library (React 19+ has native support)
|
||||
- WebSearch results for React context optimization 2026 - General patterns confirmed
|
||||
|
||||
## Metadata
|
||||
|
||||
**Confidence breakdown:**
|
||||
- Standard stack: HIGH - All optimization APIs are React built-ins, well-documented
|
||||
- Architecture: HIGH - Patterns verified against React official docs and Kent C. Dodds authoritative sources
|
||||
- Pitfalls: HIGH - Common issues documented in official React docs and multiple authoritative sources
|
||||
|
||||
**Research date:** 2026-03-05
|
||||
**Valid until:** 2026-04-05 (30 days - stable React patterns, no breaking changes expected)
|
||||
|
||||
**Key constraints:**
|
||||
- React 16.13.1 (no React 19 useContextSelector available)
|
||||
- VuContext already optimized in Phase 28 with external store
|
||||
- MixersContext identified as root cause (line 10 creates new object every render)
|
||||
- Redux already used for state management (no architectural change needed)
|
||||
Loading…
Reference in New Issue