This started as a React question and turned into a full system design conversation.
Start by clarifying requirements and constraints, then outline a high-level architecture that separates concerns: state management, streaming transport, and UI rendering. Walk through the data flow for a single message, then extend to concurrent streams and mid-stream user input, highlighting trade-offs and edge cases.
Pro tip: Emphasize idempotency and cancellation: use AbortController to cancel in-flight requests when users send new messages, and design message IDs to deduplicate streamed tokens. This shows you think about real-world reliability, not just happy paths.
Ask about expected scale (concurrent users, threads), latency requirements, and whether streaming is via WebSockets, SSE, or fetch streams. Confirm if messages need to be persisted and if there's a backend API contract.
Propose a normalized state shape: threads keyed by ID, each with an ordered list of messages; messages have status (pending, streaming, complete) and content. Use a state library like Redux, Zustand, or React Context + useReducer, ensuring efficient updates for streaming tokens.
Choose a transport (e.g., SSE or WebSocket) and handle token chunks. For each assistant message, create a placeholder and append tokens as they arrive. Use AbortController to cancel streams when users send new messages or switch threads.
Allow multiple active streams by keying streams to thread IDs. When a user sends a message mid-stream, cancel the current stream (or let it finish in background) and start a new one. Update UI optimistically and reconcile with server responses.
Use React.memo, virtualized lists, and batched state updates to avoid re-rendering entire threads on each token. Handle errors, reconnections, and race conditions (e.g., out-of-order tokens) with sequence numbers or timestamps.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Start by defining the stream context (e.g., a real-time data pipeline or WebSocket connection) and name two transient state pieces like a buffer and a session token. Explain how each is created on stream start and cleaned up on stream end or error, emphasizing resource management and idempotency.
Pro tip: Mention that cleanup must be idempotent and happen in a finally block or equivalent to handle both normal termination and failures, showing you think about reliability.
Briefly describe the stream scenario (e.g., a live data feed or chat session) to ground your answer in a concrete system.
Name two specific pieces of state that only exist during the stream, such as an in-memory buffer and a session ID, and explain their purpose.
Describe when and how each state is initialized at stream start, and how it's used during the stream.
Specify when cleanup occurs (e.g., on stream end, error, or timeout) and how you release resources (e.g., clear buffer, revoke token).
Emphasize idempotent cleanup, error handling, and avoiding resource leaks to demonstrate production readiness.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Talked about functional setState updaters so you're always appending to the latest snapshot rather than a stale closure.
Start by clarifying the scenario: streaming tokens from an API into a UI with rapid state updates. Then explain how you separate the streaming buffer from the rendered state, using techniques like batching, immutable updates, and keys to ensure smooth appends without flicker or overwrites.
Pro tip: Mention that you measure performance with React Profiler or similar tools to validate that your optimizations actually reduce re-renders and flicker, showing a data-driven approach.
Ask about the tech stack (e.g., React, Vue), update frequency, and whether tokens arrive via WebSocket or SSE. This shows you tailor solutions to context.
Explain that you accumulate incoming tokens in a mutable buffer (e.g., ref or external store) and only commit to state at controlled intervals, avoiding a state update per token.
Use requestAnimationFrame, setTimeout, or a batching library to flush the buffer to state at most once per frame, aligning with the browser's render cycle to prevent flicker.
Render tokens as a list with stable keys (e.g., index or token ID) and append immutably (e.g., [...prev, newToken]) so React reconciles efficiently without overwriting.
Use memoization (React.memo, useMemo) for token components and measure with profiling tools to confirm no unnecessary re-renders or layout shifts.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Start by acknowledging that concurrent streaming introduces shared mutable state risks, then systematically walk through failure modes like interleaved writes, race conditions, and resource contention. Emphasize isolation techniques such as per-stream context, immutable data, and synchronization primitives, and tie them to real-world streaming architectures like SSE or WebSockets.
Pro tip: Mention that you'd use a correlation ID or stream token to tag every chunk and validate it at the consumer, which prevents cross-stream contamination even if the transport layer misroutes data. This shows you think beyond just locking and consider end-to-end integrity.
Enumerate where streams might share state (e.g., buffers, session objects, global caches) and describe concrete corruption scenarios like interleaved writes, stale reads, and race conditions.
Explain how to give each stream its own context—such as per-connection objects, thread-local storage, or immutable snapshots—so that one stream's data cannot bleed into another.
Describe appropriate synchronization for unavoidable shared resources: locks, semaphores, atomic operations, or actor-model mailboxes, and discuss trade-offs like contention and deadlock risk.
Cover runtime checks like sequence numbers, checksums, or stream IDs to detect corruption, and outline recovery strategies such as stream termination, retry, or fallback to a clean state.
Mention stress testing with concurrent streams, chaos engineering, and monitoring for anomalies (e.g., latency spikes, error rates) to catch corruption in production.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Render-affecting data goes in state or a reducer.
Start by clarifying the streaming scenario (e.g., real-time data, frequent updates) and define the distinct roles of useRef (mutable value that doesn't trigger re-render) and useState (state that triggers re-render). Then, walk through concrete examples in the streaming context where each is appropriate, and conclude with a general rule based on whether the value affects rendering or needs to persist across renders without causing updates.
Pro tip: Emphasize that useRef is often used for values that are not part of the render output, such as timers, subscriptions, or previous values, while useState is for values that drive the UI. Mention that overusing useState for non-rendering values can lead to unnecessary re-renders and performance issues, especially in high-frequency streaming scenarios.
Ask or state assumptions about the streaming context: Is it real-time data? How frequent are updates? What is the UI impact? This sets the stage for trade-offs.
Briefly explain that useRef returns a mutable object whose .current property can hold any value and does not trigger re-renders, while useState returns a stateful value and a setter that triggers re-renders.
Give specific examples: useRef for storing the latest stream chunk without re-rendering, managing subscriptions, or tracking previous values; useState for updating the UI with new stream data, such as displaying the latest message or progress.
Explain performance implications: useState causes re-renders which can be costly with high-frequency updates; useRef avoids re-renders but changes won't reflect in UI. Mention patterns like throttling or batching with useState.
Conclude with a rule: Use useState when the value is part of the render output and should trigger UI updates; use useRef when the value is not used in rendering, needs to persist across renders, and mutating it should not cause re-renders.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Client-generated idempotency key (a UUID) sent with the request, server-side dedup as the real defense, and a client-side guard so a second response for the same key can't create a new message slot.
Start by clarifying the root cause: duplicate requests can stem from client-side double-submits, network retries, or React StrictMode double-invocation in development. Then propose a layered solution: idempotency keys on the server to deduplicate, client-side guards like request deduplication and optimistic UI with unique message IDs, and proper handling of retries with exponential backoff and jitter. Emphasize that the best approach depends on the specific scenario and that you'd instrument and monitor to catch duplicates in production.
Pro tip: Mention that StrictMode double-invocation is intentional in development to surface side-effect bugs, so the fix should be in the code, not by disabling StrictMode. Also, highlight that idempotency keys should be generated client-side and stored with the request, and that the server should return the same response for duplicate keys to ensure consistency.
Determine whether the duplicate request is caused by user double-click, automatic retry logic, or React StrictMode's intentional double-invocation in development. Each requires a different mitigation.
Disable the submit button after the first click, use a request deduplication library (e.g., Axios interceptors or a custom in-flight request map), and ensure effects are idempotent or cleaned up properly in StrictMode.
Generate a unique key per logical request (e.g., UUID) on the client and include it in the request header. The server stores the key and returns the cached response for duplicates, preventing duplicate side effects.
For network retries, use exponential backoff with jitter to avoid thundering herd, and ensure retries are idempotent by reusing the same idempotency key.
Assign a unique client-side ID to each message before sending, and when receiving responses, reconcile based on that ID to avoid rendering duplicates. Use a state management pattern that ignores duplicate responses.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Return a cleanup function that aborts the stream, and make sure the server-side idempotency key means the second invocation doesn't open a second real connection or produce a second response.
Explain that React 18 StrictMode intentionally double-invokes effects in development to surface non-idempotent side effects, and that opening a stream in useEffect without proper cleanup leads to duplicate connections and resource leaks. Then describe how to make setup and teardown idempotent by ensuring each effect run has a corresponding cleanup that fully tears down the stream, and by using guards like AbortController or a 'cancelled' flag to prevent stale updates.
Pro tip: Emphasize that StrictMode double-invocation is a development-only diagnostic, not a bug, and that writing idempotent effects also improves resilience in production (e.g., Fast Refresh, concurrent rendering). Mention that you can verify idempotency by logging connection counts or using React DevTools to inspect effect runs.
Explain that React 18 StrictMode mounts, unmounts, and remounts components in development to help detect side effects that aren't properly cleaned up. This means useEffect runs twice: setup, cleanup, setup again.
Opening a stream in useEffect without cleanup creates two concurrent streams, leading to duplicate data, memory leaks, and race conditions. The second setup may also conflict with the first if not properly isolated.
Ensure that each effect run creates a self-contained stream instance and returns a cleanup function that fully closes that instance. Use AbortController or a local 'cancelled' flag to ignore events from stale streams.
Write the effect with proper cleanup, then test in StrictMode to confirm only one active stream remains after the double-invocation. Use logging or DevTools to verify no leaks.
Note that while StrictMode only affects development, the patterns you use (idempotent effects) also prevent issues in production with Fast Refresh or future concurrent features. Mention alternatives like using a ref to track initialization if appropriate.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Offered three options: resume from an offset (needs server support for a byte range or sequence number), restart from scratch with a new stream, or surface the partial message as-is with an error state.
Start by clarifying the streaming protocol (e.g., SSE, WebSocket, HTTP chunked) and the nature of the drop (network, server, client). Then outline a recovery strategy that includes resuming from the last received token/byte using a cursor or offset, with idempotency and deduplication. Finally, specify the server contract requirements: resumable endpoints, sequence numbers, heartbeats, and error signaling.
Pro tip: Emphasize that recovery must be idempotent and that the server should support resuming from a client-provided cursor; this shows you think about correctness and efficiency, not just reconnecting.
Identify the streaming protocol (SSE, WebSocket, HTTP chunked) and the failure mode (network blip, server restart, client crash). This determines the recovery mechanism.
On disconnect, the client should attempt to reconnect with exponential backoff and resume from the last successfully processed chunk using a cursor (e.g., sequence number or byte offset). Buffer unacknowledged data and handle duplicates.
The server must support a resumable endpoint that accepts a cursor and returns the stream from that point. It should include sequence numbers or offsets in each chunk, and provide a way to signal stream end or errors.
Use idempotent operations and deduplication on the client to avoid processing the same chunk twice. The server should guarantee that resuming from a cursor yields exactly the missing data without gaps or duplicates.
Address scenarios like cursor expiration, server-side state loss, and partial writes. Implement heartbeats to detect dead connections and log recovery attempts for observability.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
React DevTools profiler to find what's re-rendering, then: buffer tokens and flush on an interval rather than per-token, memoize the static parts of the message list so only the actively streaming message re-renders, virtualize the list if it gets long.
Start by describing how you would measure and confirm the performance bottleneck, then systematically walk through rendering optimization techniques. Emphasize React-specific solutions like virtualization, memoization, and batching, and connect them to the trade-offs in a long message list scenario.
Pro tip: Mention that you would first reproduce the issue with a profiler and quantify the cost per token before optimizing, because premature optimization can lead to unnecessary complexity. Also, highlight that the best solution often combines virtualization with memoization and avoiding unnecessary re-renders at the component level.
Use React DevTools Profiler and browser performance tools to identify which components re-render on each token and measure the time spent. Confirm that rendering is the bottleneck, not network or state management.
Determine why every token causes a re-render: e.g., state updates at a high level, lack of memoization, or inefficient list rendering. Check if the entire list re-renders or only the new message.
Implement techniques like virtualization (react-window, react-virtualized), React.memo for list items, useCallback/useMemo to stabilize props, and key extraction. Consider windowing to render only visible messages.
Move state closer to where it's needed, use context selectors or state management libraries with fine-grained subscriptions, and batch updates to avoid multiple re-renders per token.
Re-measure performance after each change, ensure no regressions, and consider trade-offs like added complexity vs. performance gains. Test with realistic message volumes.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Said I'd put it in a custom hook backed by an external store (something like Zustand or a hand-rolled pub-sub).
Start by clarifying the scope of the streaming state machine and the constraints (e.g., real-time updates, cross-component sharing, persistence). Then compare external state managers (Redux, Zustand, XState, MobX) and non-React stores (custom event emitters, RxJS) against useReducer, focusing on trade-offs in complexity, performance, and testability. Conclude with a recommendation based on the specific needs of the streaming use case.
Pro tip: Emphasize that moving state out of React can reduce unnecessary re-renders and enable state sharing across components, but it introduces synchronization challenges and may require manual subscription management. Show that you consider the team's familiarity and long-term maintainability, not just technical purity.
Identify what the streaming state machine manages (e.g., connection status, buffered chunks, playback position) and its constraints (e.g., high-frequency updates, need for persistence, cross-component access).
Compare options like Redux, Zustand, XState, MobX, or a custom store (e.g., event emitter, RxJS) based on their fit for streaming state, including reactivity, performance, and dev tools.
Discuss gains: decoupling from React, easier sharing, persistence, and testability. Losses: added complexity, potential over-engineering, loss of React's concurrent features, and need for manual subscription management.
Explain how to integrate the external store with React (e.g., useSyncExternalStore, context) and how to migrate incrementally without disrupting the app.
Provide a clear recommendation, justifying it with the specific needs of the streaming state machine and the team's context, and acknowledge any remaining risks.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.