Repository navigation
Custom Hooks
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendices
This document explains PersonalLearningPro’s custom hook architecture with a focus on:
- use-chat-ws: WebSocket connection management, real-time message handling, and connection state synchronization
- use-mobile: responsive design detection and mobile-specific UI adaptations
- use-toast: notification management and user feedback systems
It covers hook composition patterns, dependency management, performance optimizations, testing strategies, error handling, and integration with React’s rendering lifecycle. Practical usage examples are included to demonstrate how these hooks are applied across components.
The hooks live under client/src/hooks and are consumed by UI components and pages. The App initializes providers and global UI elements, including the toast renderer.
graph TB
subgraph "Hooks"
U1["use-chat-ws.ts"]
U2["use-mobile.tsx"]
U3["use-toast.ts"]
end
subgraph "UI Components"
C1["ChatLayout.tsx"]
C2["ChatThread.tsx"]
C3["sidebar.tsx"]
C4["toaster.tsx"]
C5["toast.tsx"]
end
subgraph "App"
A1["App.tsx"]
end
A1 --> C1
A1 --> C2
A1 --> C3
A1 --> C4
C4 --> C5
C1 --> U1
C2 --> U1
C3 --> U2
A1 --> U3
C4 --> U3
C5 --> U3
Diagram sources
- App.tsx
- ChatLayout.tsx
- ChatThread.tsx
- sidebar.tsx
- toaster.tsx
- toast.tsx
- use-chat-ws.ts
- use-mobile.tsx
- use-toast.ts
Section sources
- App.tsx
- use-chat-ws: Manages a WebSocket connection, joins/leaves channels, sends/receives typed events, reconnects with exponential backoff, and exposes typed helpers for messaging actions.
- use-mobile: Detects mobile viewport and adapts UI behavior accordingly.
- use-toast: Centralized toast manager with a Redux-like reducer, queue-based dismissal, and a provider-renderer pair.
Section sources
- use-chat-ws.ts
- use-mobile.tsx
- use-toast.ts
The hooks integrate with React components and providers to deliver real-time chat, responsive UI, and user feedback.
sequenceDiagram
participant Comp as "Component"
participant Hook as "use-chat-ws"
participant WS as "WebSocket Server"
participant UI as "UI Thread"
Comp->>Hook : "useChatWs({ channelId, callbacks })"
Hook->>WS : "connect() and optional join_channel"
WS-->>Hook : "onmessage(type=new_message|typing|read|presence)"
Hook->>Comp : "invoke callbacks (onNewMessage/onTyping/onRead/onPresence)"
Comp->>UI : "update state and render"
Hook->>WS : "sendMessage/sendTyping/markRead"
WS-->>Hook : "ack/error"
Hook->>Hook : "reconnect with exponential backoff"
Diagram sources
- use-chat-ws.ts
- ChatThread.tsx
Purpose:
- Establish and maintain a WebSocket connection
- Join/leave channels dynamically
- Deliver typed incoming events to callbacks
- Provide typed helpers to send messages, typing indicators, and read receipts
Key behaviors:
- Connection lifecycle: connect on mount, close on unmount, and cancel pending reconnects
- Channel lifecycle: join on mount and leave on unmount; join/leave on channelId changes
- Reconnection: exponential backoff up to a cap, with special handling for unauthorized closures
- Event routing: parses JSON messages and dispatches to registered callbacks
- Public API: sendMessage, sendTyping, markRead
flowchart TD
Start(["Mount"]) --> Connect["connect()"]
Connect --> Open{"WebSocket OPEN?"}
Open --> |No| Backoff["Exponential backoff"]
Backoff --> Connect
Open --> |Yes| Join["Join channel if provided"]
Join --> Listen["Listen for onmessage"]
Listen --> Type{"Event type?"}
Type --> |new_message| OnMsg["Invoke onNewMessage"]
Type --> |user_typing| OnTyping["Invoke onTyping"]
Type --> |message_read| OnRead["Invoke onRead"]
Type --> |user_presence| OnPresence["Invoke onPresence"]
Type --> |error| Warn["Console warn"]
OnMsg --> End
OnTyping --> End
OnRead --> End
OnPresence --> End
Warn --> End
End(["Unmount or channel change"]) --> Close["Close and cleanup"]
Diagram sources
- use-chat-ws.ts
- use-chat-ws.ts
Usage examples:
- ChatLayout establishes a long-lived connection bound to the active conversation channel.
- ChatThread consumes callbacks to synchronize UI state and integrates with a caching layer.
sequenceDiagram
participant Layout as "ChatLayout"
participant Hook as "use-chat-ws"
participant Thread as "ChatThread"
participant WS as "WebSocket Server"
Layout->>Hook : "useChatWs({ channelId })"
Hook->>WS : "join_channel"
WS-->>Hook : "new_message"
Hook-->>Thread : "onNewMessage"
Thread->>Thread : "merge optimistic and server messages"
WS-->>Hook : "user_typing"
Hook-->>Thread : "onTyping"
WS-->>Hook : "message_read"
Hook-->>Thread : "onRead"
Diagram sources
- ChatLayout.tsx
- ChatThread.tsx
- use-chat-ws.ts
Best practices:
- Always pass a stable callback ref for event handlers to avoid stale closures
- Guard send operations by readiness state
- Use channel lifecycle effects to join/leave appropriately
- Respect unauthorized disconnects and avoid reconnect loops
Section sources
- use-chat-ws.ts
- ChatLayout.tsx
- ChatThread.tsx
Purpose:
- Detect whether the viewport width is below a mobile breakpoint
- Provide a boolean flag to adapt UI behavior
Implementation highlights:
- Uses matchMedia to track viewport changes
- Initializes state on mount and cleans up listeners on unmount
- Returns a boolean coerced value
flowchart TD
MStart(["Mount"]) --> Query["matchMedia(max-width: 767px)"]
Query --> Init["Set initial isMobile"]
Init --> Change["onChange: recalc isMobile"]
Change --> Cleanup["Remove listener on unmount"]
Diagram sources
- use-mobile.tsx
Usage example:
- Sidebar uses the hook to decide between overlay modal and desktop sidebar behavior.
Section sources
- use-mobile.tsx
- sidebar.tsx
Purpose:
- Provide a global toast notification system with a simple imperative API
- Manage toast lifecycle, limits, and dismissal queues
Core mechanics:
- Internal reducer manages a bounded list of toasts
- Queue-based removal timers per toast id
- Provider-renderer pattern: a dedicated component subscribes to state and renders Radix toast primitives
sequenceDiagram
participant UI as "Component"
participant Hook as "useToast()"
participant Store as "Reducer + Listeners"
participant Renderer as "Toaster"
UI->>Hook : "toast({ title, description, ... })"
Hook->>Store : "dispatch(ADD_TOAST)"
Store-->>Renderer : "listeners notify"
Renderer-->>UI : "Toast visible"
UI->>Hook : "dismiss(toastId)"
Hook->>Store : "dispatch(DISMISS_TOAST)"
Store-->>Renderer : "listeners notify"
Renderer-->>UI : "Toast dismissed"
Diagram sources
- use-toast.ts
- toaster.tsx
- toast.tsx
Usage examples:
- Authentication flows show success and error notifications after login/register/logout actions.
- OCR processing demonstrates success and informational toasts during user interactions.
Section sources
- use-toast.ts
- toaster.tsx
- toast.tsx
- firebase-auth-context.tsx
- ocr-processing.tsx
- use-chat-ws depends on:
- React refs and effects for connection lifecycle
- WebSocket API for transport
- Optional external caching layer (React Query) in consumers
- use-mobile depends on:
- matchMedia for responsive queries
- use-toast depends on:
- React state and effects for subscription
- Radix UI toast primitives for rendering
graph LR
CM["ChatThread.tsx"] --> HC["use-chat-ws.ts"]
CL["ChatLayout.tsx"] --> HC
SB["sidebar.tsx"] --> UM["use-mobile.tsx"]
TA["toaster.tsx"] --> UT["use-toast.ts"]
APP["App.tsx"] --> TA
Diagram sources
- ChatThread.tsx
- ChatLayout.tsx
- sidebar.tsx
- toaster.tsx
- use-toast.ts
- App.tsx
Section sources
- ChatThread.tsx
- ChatLayout.tsx
- sidebar.tsx
- toaster.tsx
- use-toast.ts
- App.tsx
- use-chat-ws
- Uses memoized callbacks and refs to avoid stale closures and unnecessary re-renders
- Limits reconnection attempts with exponential backoff to reduce resource contention
- Guards send operations by ready state to prevent errors and wasted work
- use-mobile
- Efficiently tracks media queries and updates only on change events
- use-toast
- Limits concurrent toasts to a small number
- Queues removal timers per toast id to avoid global re-renders on dismissal
[No sources needed since this section provides general guidance]
- use-chat-ws
- Unauthorized disconnects (code 4001) halt reconnection attempts; verify authentication and session validity
- If messages do not appear, confirm the channel id is set and the connection is open before sending
- For stale callbacks, ensure event handlers are passed via refs and updated on each render
- use-mobile
- If the hook does not reflect viewport changes, verify the media query listener is attached and cleaned up
- use-toast
- If toasts do not dismiss automatically, check that the global timer queue is initialized and timers are scheduled
- If multiple toasts overlap, confirm the limit is enforced and older toasts are removed
Section sources
- use-chat-ws.ts
- use-mobile.tsx
- use-toast.ts
These hooks encapsulate cross-cutting concerns—real-time communication, responsive behavior, and user feedback—into reusable, composable units. They leverage React patterns (refs, effects, callbacks) and integrate cleanly with UI providers and external libraries. Following the recommended patterns ensures predictable behavior, robust error handling, and optimal performance.
[No sources needed since this section summarizes without analyzing specific files]
- use-chat-ws
- Compose with UI components that require real-time updates
- Combine with caching libraries to reconcile optimistic and server data
- use-mobile
- Compose with layout components to switch between mobile and desktop modes
- use-toast
- Compose with any component needing user feedback, especially in async flows
[No sources needed since this section provides general guidance]
- use-chat-ws
- Mock WebSocket and intercept send calls to assert payload correctness
- Simulate onmessage events to verify callback invocation
- Test reconnection behavior by simulating close events with different codes
- use-mobile
- Mock matchMedia and simulate viewport resize events
- use-toast
- Assert reducer transitions and listener updates
- Verify toast limits and automatic dismissal timers
[No sources needed since this section provides general guidance]
- All hooks rely on useEffect for side effects and cleanup
- Callback refs ensure event handlers remain current across renders
- Provider-renderer pattern for toasts ensures global state updates are reflected immediately
Section sources
- use-chat-ws.ts
- use-toast.ts
- toaster.tsx