8.8 KiB
8.8 KiB
Redux Migration Guide for JKSessionScreen
Files Created
1. src/store/features/activeSessionSlice.js
Redux slice for managing the active session state including:
- Session data and lifecycle (joining, leaving)
- Participants management
- User tracks, jam tracks, and backing tracks
- Recording state
- Connection status
Key Actions:
fetchActiveSession(sessionId)- Async thunk to fetch session datajoinActiveSession({ sessionId, options })- Async thunk to join sessionleaveActiveSession(sessionId)- Async thunk to leave sessionaddParticipant(participant)- Add a participant to the sessionremoveParticipant(participantId)- Remove a participantaddUserTrack(track)- Add a user tracksetSelectedJamTrack(jamTrack)- Set the selected jam trackstartRecording(recordingId)- Start recordingstopRecording()- Stop recordingsetConnectionStatus(status)- Update connection statusclearSession()- Clear all session state
Key Selectors:
selectActiveSession(state)- Get session dataselectJoinStatus(state)- Get join statusselectHasJoined(state)- Check if user has joinedselectParticipants(state)- Get participants listselectUserTracks(state)- Get user tracksselectIsRecording(state)- Check if recording is activeselectConnectionStatus(state)- Get connection status
2. src/store/features/sessionUISlice.js
Redux slice for managing session UI state including:
- Modal visibility (settings, invite, volume, recording, leave, etc.)
- Panel visibility (chat, mixer, participants, tracks)
- View preferences (layout, video visibility, sidebar state)
Key Actions:
openModal(modalName)- Open a specific modalcloseModal(modalName)- Close a specific modaltoggleModal(modalName)- Toggle a modalcloseAllModals()- Close all modalstogglePanel(panelName)- Toggle a panelsetParticipantLayout(layout)- Set participant layout ('grid' | 'list' | 'compact')toggleMixer()- Toggle mixer visibilityresetUI()- Reset all UI state
Key Selectors:
selectModal(modalName)(state)- Check if a modal is openselectIsAnyModalOpen(state)- Check if any modal is openselectPanel(panelName)(state)- Check if a panel is openselectShowMixer(state)- Check if mixer is visibleselectParticipantLayout(state)- Get participant layout
3. src/hooks/useSessionWebSocket.js
Custom hook that integrates WebSocket messages with Redux actions. This hook:
- Listens to WebSocket events from
jamServer - Dispatches appropriate Redux actions when events occur
- Handles cleanup on unmount
Usage:
import { useSessionWebSocket } from '../hooks/useSessionWebSocket';
function JKSessionScreen() {
const { sessionId } = useParams();
// This hook will automatically dispatch Redux actions
// when WebSocket events are received
useSessionWebSocket(sessionId);
// ... rest of component
}
4. Updated src/store/store.js
Added the new reducers to the Redux store:
activeSession- Active session statesessionUI- Session UI state
Migration Strategy
Phase 1: Quick Wins - Migrate Modal State (Start Here)
Replace useState for modals with Redux:
Before:
const [showSettingsModal, setShowSettingsModal] = useState(false);
const [showInviteModal, setShowInviteModal] = useState(false);
// In JSX
<button onClick={() => setShowSettingsModal(true)}>Settings</button>
{showSettingsModal && <SettingsModal onClose={() => setShowSettingsModal(false)} />}
After:
import { useDispatch, useSelector } from 'react-redux';
import { openModal, closeModal, selectModal } from '../../store/features/sessionUISlice';
const dispatch = useDispatch();
const showSettingsModal = useSelector(selectModal('settings'));
// In JSX
<button onClick={() => dispatch(openModal('settings'))}>Settings</button>
{showSettingsModal && <SettingsModal onClose={() => dispatch(closeModal('settings'))} />}
Benefits:
- Removes 5-8 useState hooks immediately
- Easy to test
- Good practice for team
Phase 2: Migrate Session Lifecycle
Replace session join/leave logic with Redux thunks:
Before:
const [hasJoined, setHasJoined] = useState(false);
const [sessionGuardsPassed, setSessionGuardsPassed] = useState(false);
const handleJoinSession = async () => {
// Guard checks...
setSessionGuardsPassed(true);
const response = await joinSession(sessionId, options);
setHasJoined(true);
};
After:
import { useDispatch, useSelector } from 'react-redux';
import {
joinActiveSession,
selectJoinStatus,
selectHasJoined
} from '../../store/features/activeSessionSlice';
const dispatch = useDispatch();
const joinStatus = useSelector(selectJoinStatus);
const hasJoined = useSelector(selectHasJoined);
const handleJoinSession = async () => {
// Guard checks...
await dispatch(joinActiveSession({ sessionId, options }));
};
Phase 3: Migrate Entity Collections
Replace useState arrays with Redux:
Before:
const [userTracks, setUserTracks] = useState([]);
const [participants, setParticipants] = useState([]);
// When WebSocket message arrives
jamServer.on('trackAdded', (data) => {
setUserTracks([...userTracks, data.track]);
});
After:
import { useDispatch, useSelector } from 'react-redux';
import { selectUserTracks, selectParticipants } from '../../store/features/activeSessionSlice';
import { useSessionWebSocket } from '../../hooks/useSessionWebSocket';
const userTracks = useSelector(selectUserTracks);
const participants = useSelector(selectParticipants);
// WebSocket integration is automatic via custom hook
useSessionWebSocket(sessionId);
// No manual event handlers needed!
Phase 4: Replace CurrentSessionContext
Once most state is in Redux, you can simplify or remove CurrentSessionContext:
Before:
const { currentSession, setCurrentSession, refreshSession } = useCurrentSessionContext();
After:
import { useSelector, useDispatch } from 'react-redux';
import { selectActiveSession, fetchActiveSession } from '../../store/features/activeSessionSlice';
const currentSession = useSelector(selectActiveSession);
const dispatch = useDispatch();
// To refresh
dispatch(fetchActiveSession(sessionId));
Testing Your Migration
Check Redux DevTools
Install Redux DevTools browser extension to:
- View the entire state tree
- See every action dispatched
- Time-travel debug state changes
- Export/import state for testing
Verify State Updates
After each phase, verify:
// Check if state is in Redux
console.log('Redux State:', store.getState().activeSession);
console.log('Redux UI:', store.getState().sessionUI);
Test WebSocket Integration
In JKSessionScreen, verify the custom hook is working:
useSessionWebSocket(sessionId);
// Watch Redux DevTools - you should see actions dispatched
// when WebSocket events occur
Common Patterns
Pattern 1: Conditional Rendering Based on Redux State
const isRecording = useSelector(selectIsRecording);
const connectionStatus = useSelector(selectConnectionStatus);
return (
<>
{connectionStatus === 'disconnected' && <ConnectionAlert />}
{isRecording && <RecordingIndicator />}
</>
);
Pattern 2: Dispatching Multiple Actions
const handleLeaveSession = async () => {
// Close all modals first
dispatch(closeAllModals());
// Leave the session
await dispatch(leaveActiveSession(sessionId));
// Navigate away
navigate('/dashboard');
};
Pattern 3: Using Multiple Selectors
const session = useSelector(selectActiveSession);
const participants = useSelector(selectParticipants);
const tracks = useSelector(selectUserTracks);
const showMixer = useSelector(selectModal('mixer'));
// All selectors are memoized, so re-renders only happen when data changes
Next Steps
- Start with Phase 1: Migrate modal state first - it's the easiest and gives immediate value
- Test thoroughly: After each phase, test the UI and check Redux DevTools
- Document as you go: Update component documentation with Redux patterns
- Remove old contexts gradually: Once state is migrated, clean up unused contexts
Notes
- The WebSocket event names in
useSessionWebSocket.jsare examples. Update them to match your actual Protocol Buffer message types. - The
jamServerAPI methods (on,off) may differ based on your implementation. Adjust accordingly. - Consider creating additional custom hooks for specific features (e.g.,
useSessionRecording,useSessionTracks) that encapsulate Redux logic.
Questions?
If you need help with:
- Specific WebSocket message types and how to handle them
- Complex state transformations
- Performance optimization with selectors
- Testing Redux logic
Just ask!