State Management with Hooks
Relevant source files
The following files were used as context for generating this wiki page:
The application employs a decentralized state management strategy using custom React Hooks. Instead of a global state container, the application's state is partitioned by domain and encapsulated within specific hooks. This approach promotes modularity and co-locates state logic with the features that use it. The main App component acts as an orchestrator, consuming these hooks and propagating state and actions down to its child components.
The core of this architecture revolves around three primary hooks: useTranscript, useNotes, and useContentGenerator. Each hook is responsible for a distinct slice of the application's data, including fetching, processing, and persistence logic.
Sources: App.tsx:27-29
Core Hooks Architecture
The application's state is managed by a collection of custom hooks consumed at the top level by the App component. This diagram illustrates the relationship between the main component and the primary state management hooks.
This diagram shows the App component initializing the three main hooks. The state and dispatch functions returned by these hooks are then passed as props to the relevant child components.
Sources: App.tsx:27-29, App.tsx:206-226
useTranscript
This hook manages the state related to the video transcript, including its retrieval and loading status.
Sources: hooks/useTranscript.ts
State and Functions
| Name | Type | Description |
|---|---|---|
transcript | TranscriptSegment[] | An array of transcript segments, each with text and a start time. |
isLoading | boolean | A flag indicating if the transcript is currently being fetched. |
error | string | null | Stores an error message if the transcript fetch fails. |
loadTranscript | (videoId: string) => Promise<void> | An async function that fetches the transcript for a given video ID. |
resetTranscript | () => void | A function to clear the current transcript, error, and loading state. |
Sources: hooks/useTranscript.ts:8-13, hooks/useTranscript.ts:26-49
Data Flow: Loading a Transcript
The loadTranscript function initiates the data fetching process. It calls YoutubeService.fetchTranscript, which makes a request to the backend API. It also includes fallback logic to load a demo transcript if the API call fails for the demo video ID.
Sources: hooks/useTranscript.ts:26-49, App.tsx:135-138
useNotes
This hook is responsible for creating, managing, and persisting user-generated "margin notes." It handles the interaction with localStorage for storage and the Gemini AI service for cleaning up transcript text.
Sources: hooks/useNotes.ts
State and Functions
| Name | Type | Description |
|---|---|---|
notes | Note[] | An array of saved note objects. |
processingStatus | ProcessingStatus | An enum (IDLE, PROCESSING, SUCCESS, ERROR) indicating the status of the addNote operation. |
loadNotes | (videoId: string) => void | Loads notes for a specific video from storageService. |
addNote | (...) => Promise<{...}> | Creates a new note by cleaning the selected text with an AI service and saving it. |
deleteNote | (videoId: string, noteId: string) => void | Deletes a note from storageService. |
Sources: hooks/useNotes.ts:14-19, types.ts:15-20
Data Flow: Adding a Note
The addNote function is triggered when a user analyzes a selection of transcript text. It first checks for an API key, then calls the cleanTranscriptText service, creates a new note object, and saves it via the storageService.
Sources: hooks/useNotes.ts:25-50
useContentGenerator
This hook manages the generation and caching of AI-produced content for the "Smart Board," such as Twitter threads, LinkedIn posts, and key takeaways. It orchestrates parallel API calls to the Gemini service and persists the results in localStorage to avoid re-generation for the same video.
Sources: hooks/useContentGenerator.ts
State and Functions
| Name | Type | Description |
|---|---|---|
content | ContentOutput | null | An object containing all generated content (thread, post, etc.). |
isGenerating | boolean | A flag indicating if any content generation is in progress. |
progress | GenerationProgress | An object tracking the status ('idle', 'loading', 'done', 'error') of each content type. |
generateAll | (transcript: string) => Promise<void> | Triggers parallel generation of all content types from the full transcript. |
clearContent | () => void | Clears the generated content from state and localStorage. |
hasContent | boolean | A derived boolean indicating if any content has been generated or loaded. |
Sources: hooks/useContentGenerator.ts:18-25
Logic Flow: generateAll
The generateAll function is the primary entry point for this hook. It is automatically called when a new transcript is loaded. It runs all generation functions in parallel using Promise.allSettled to ensure that the failure of one does not block the others.
Sources: hooks/useContentGenerator.ts:70-130
Local Storage Caching
The useContentGenerator hook uses useEffect to automatically manage caching in localStorage.
- On Load: When the
videoIdchanges, auseEffecthook attempts to load previously generated content fromlocalStorageusing the keytldw_content_${videoId}. - On Change: When the
contentstate is updated (after a successful generation), anotheruseEffecthook saves the new content object tolocalStorage.
This ensures that once content is generated for a video, it is immediately available on subsequent visits without needing to call the AI service again.
Sources: hooks/useContentGenerator.ts:33-51
Data Models
The state managed by these hooks conforms to specific data structures defined in types.ts.
This diagram shows the primary data entities. ContentOutput is the main structure for AI-generated content. Note represents a single user-created note, and TranscriptSegment is the building block of the video transcript.
Sources: types.ts
Conclusion
The application's state management is built on a robust and scalable pattern using React Hooks. By separating concerns into useTranscript, useNotes, and useContentGenerator, the codebase remains organized and easy to maintain. The App component serves as a clean integration point, composing these hooks to build the application's features. This architecture effectively manages asynchronous operations, data persistence, and UI state without the overhead of a third-party state management library.
Sources: App.tsx:27-29