Comprehensive Fix Plan for Sasha Studio Re-Render Issues
Version: 1.0.0
Status: ACTIVE
Date: 2025-01-29
Priority: CRITICAL
Executive Summary
Sasha Studio currently experiences 10-20 component re-renders per user action due to replacing entire message arrays when only one message is added. This plan consolidates all technical documents into a single, actionable implementation guide focused on message streaming as the primary solution.
Related Documents
- MIGRATION-plan.md - Detailed 4-week migration timeline
- ARCHITECTURE-target.md - Target architecture specification
- MESSAGE-ARRAY-DESIGN.md - Message streaming rationale
- REQUIREMENTS-v2.md - Functional requirements
The Core Problem
// CURRENT: Every message update replaces entire array
messages: [...100 existing messages, 1 new message] // New array = React re-renders everything
// TARGET: Stream individual messages
message: { id: 'msg-101', content: '...', isStreaming: true } // Append only = minimal re-render
Priority Implementation Order
π΄ CRITICAL PATH (Must Do First)
Week 1: Foundation (3 days)
Fix useEffect dependencies (
App.jsxlines 128-209)- Split 80-line mega useEffect
- Use primitives:
[selectedProject?.id]not[projects] - Impact: 30% reduction in re-renders
Remove unused flags (
projectReducer.js)- Delete
isProcessingWebSocket,isNavigating - Impact: Cleaner state, fewer updates
- Delete
Clean storage
- Remove
sasha_chat_messages_*from localStorage - Impact: Eliminates duplicate state sources
- Remove
Week 2: Message Streaming (5 days) MOST CRITICAL
This phase alone solves 70% of performance issues.
- Create JSONL incremental reader (
server/services/message-stream.js)
class MessageStreamReader {
async readNewMessages(filePath, fromPosition) {
const stream = fs.createReadStream(filePath, { start: fromPosition });
// Read only NEW lines, not entire file
return newMessages;
}
}
- Implement streaming WebSocket (
server/index.js)
// STOP sending this:
io.emit('projects_updated', { projects: [...] }); // 100KB
// START sending this:
io.emit('message_streamed', {
sessionId: 'xxx',
message: { id: 'msg-123', content: '...' } // 500 bytes
});
- Update reducer for streaming (
projectReducer.js)
case 'APPEND_MESSAGE':
// Append single message without replacing array
return {
...state,
messageStreams: {
[sessionId]: {
messages: [...existing, newMessage], // Append only
isStreaming: true
}
}
};
Week 3: Smart File Watcher (3 days)
Route by file type (
server/services/smart-watcher.js)- Docs β Toast only (no re-render)
- Messages β Stream update (1-2 re-renders)
- Files β Tree update only
Type-specific debouncing
- Messages: 50ms (real-time feel)
- Documentation: 1000ms (batch changes)
- Files: 500ms (balanced)
Week 4: Testing & Rollout (2 days)
Performance validation
- Measure with React DevTools Profiler
- Target: 1-2 re-renders per message
- WebSocket payload <500 bytes
Feature flag rollout
- 10% β 50% β 100% gradual deployment
π‘ IMPORTANT (But Not Critical Path)
- URL routing (
/project/{name}/chat/{id}) - React.memo optimization
- Virtual scrolling for 1000+ messages
π’ NICE TO HAVE (Do Later)
- Documentation HTML generation optimization
- Advanced caching strategies
- IndexedDB for offline support
File Modification Priority
Must Change First (High Impact)
src/App.jsx- Split useEffect (lines 128-209)src/reducers/projectReducer.js- Add streaming actionsserver/index.js- Implement message streaming
Must Create New
server/services/message-stream.js- JSONL readerserver/services/smart-watcher.js- Intelligent routing
Can Update Later
src/components/ChatInterface.jsx- Optimize renderingsrc/hooks/useProjectWebSocketV2.js- Handle streaming
Success Metrics
Performance Targets (MUST ACHIEVE)
| Metric | Current | Week 2 Target | Final Target |
|---|---|---|---|
| Re-renders per message | 10-20 | 3-5 | 1-2 |
| WebSocket payload | ~100KB | <5KB | <500 bytes |
| Message latency | 800ms | 200ms | <100ms |
| File fetches | 10-40 | 5 | 1 |
How to Measure
- Open React DevTools Profiler
- Start recording
- Send a message to Claude
- Stop recording
- Count component renders (target: 1-2)
Implementation Checklist
Week 1: Foundation
- Split App.jsx mega useEffect
- Fix all useEffect dependencies
- Remove unused reducer flags
- Clean localStorage/sessionStorage
- Add performance logging
Week 2: Message Streaming (CRITICAL) π΄
- Create MessageStreamReader class
- Track JSONL file positions
- Send individual messages via WebSocket
- Implement APPEND_MESSAGE action
- Test with real Claude responses
- Verify <500 byte payloads
- Confirm 1-2 re-renders
Week 3: Smart Watcher
- Create smart-watcher.js
- Route by file type
- Implement type-specific debouncing
- Documentation β toast only
- Files β tree update only
Week 4: Validation
- Run performance tests
- Deploy with feature flag
- Monitor metrics
- Gradual rollout
Common Pitfalls to Avoid
DON'T
- Send entire message arrays
- Use objects as useEffect dependencies
- Replace unchanged state references
- Debounce messages longer than 50ms
- Skip performance measurement
DO
- Stream individual messages
- Use primitive dependencies
- Preserve object references
- Keep message debounce at 50ms
- Measure before/after each change
Quick Start Commands
# 1. Check current performance
npm run dev
# Open React DevTools, measure re-renders
# 2. Implement message streaming
# Edit server/index.js and projectReducer.js
# 3. Test improvements
# Send messages, measure again
# 4. Deploy with flag
ENABLE_MESSAGE_STREAMING=true npm run dev
Risk Mitigation
- Feature Flags: All changes behind
ENABLE_MESSAGE_STREAMINGflag - Backward Compatibility: Support both old and new message formats
- Rollback Plan: Can disable streaming instantly
- Monitoring: Log performance metrics before/after
Expected Timeline
- Day 1-3: Foundation fixes (30% improvement)
- Day 4-8: Message streaming (70% improvement) π΄
- Day 9-11: Smart watcher (10% improvement)
- Day 12-15: Testing and rollout
Total time: 15 working days (3 weeks)
Critical path: Days 4-8 (message streaming)
Conclusion
The key insight is that message streaming alone solves 70% of the problem. By sending individual messages instead of entire arrays, we eliminate the root cause of excessive re-renders. Everything else is optimization on top of this core fix.
Start with Week 2 (message streaming) if you only have time for one change.