Select 4-6 hooks you've genuinely used in production and for each, briefly describe the use case, then dive into a specific bug you encountered, how you diagnosed it, and the fix. Prioritize hooks that demonstrate depth (e.g., useEffect, useMemo, useCallback, useReducer, useContext, custom hooks) and tie bugs to real-world impact like performance or correctness. Keep the narrative structured and concise, showing both technical knowledge and debugging maturity.
Pro tip: Choose bugs that reveal a non-obvious React pitfall (e.g., stale closures, dependency array mistakes, or unnecessary re-renders) and explain how you used React DevTools or profiling to pinpoint the issue. This shows you don't just code—you debug systematically and understand the 'why' behind React's behavior.
Pick 4-6 hooks you've truly used in production, ordering them from most to least complex or impactful. Avoid listing every hook you know; focus on ones where you have a compelling bug story.
For each hook, give a one-sentence summary of the production scenario (e.g., 'useEffect for syncing data with a WebSocket connection in a dashboard').
Explain the bug clearly: what went wrong, how it manifested (e.g., infinite loop, stale data, memory leak), and its impact on users or performance.
Walk through how you identified the root cause (e.g., using React DevTools, console logs, or code review) and the exact fix you applied, including any trade-offs.
Conclude each hook's story with a key takeaway or best practice you now follow, demonstrating growth and deeper understanding.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
I talked about stale closures and forgetting to include functions in the dep array.
Start by acknowledging that useEffect dependency array mistakes are common and can lead to bugs like stale closures, infinite loops, and unnecessary re-renders. Then, systematically explain the most frequent mistakes, why they happen, and how to avoid them, emphasizing the importance of understanding React's reconciliation and effect lifecycle. Conclude with best practices and tools for catching these issues.
Pro tip: Mention that using the exhaustive-deps ESLint rule is a good start, but it's not a silver bullet; sometimes you need to restructure your code or use useCallback/useMemo to stabilize dependencies. Also, highlight that understanding the 'why' behind the rule helps in making informed trade-offs.
List the typical errors: missing dependencies, unnecessary dependencies, incorrect dependencies (e.g., objects/functions recreated each render), and misunderstanding when the effect runs.
Describe the bugs each mistake causes: stale props/state, infinite loops, performance issues, and unpredictable behavior.
Offer ways to fix each mistake: using the exhaustive-deps lint rule, memoizing values with useCallback/useMemo, moving functions inside effects, and using refs for mutable values.
Talk about when to intentionally omit dependencies (e.g., for one-time effects) and how to handle complex dependencies with custom hooks or reducers.
Provide a concrete example (e.g., fetching data) that illustrates a common mistake and its fix, showing practical understanding.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Blanked for a second on a concrete example.
Start by defining what 'helping performance' means in React—avoiding unnecessary re-renders or expensive recalculations—then explain that useMemo/useCallback only pay off when the cost of the memoization overhead is less than the cost of the work being memoized. Use concrete examples to illustrate when they are beneficial and when they are unnecessary noise.
Pro tip: Emphasize that premature optimization with useMemo/useCallback can actually hurt performance due to increased memory usage and dependency comparison overhead, and that profiling with React DevTools should guide their usage.
Explain that useMemo memoizes a computed value and useCallback memoizes a function reference, both to maintain referential equality across renders.
They help when passing callbacks or objects to optimized child components (React.memo, PureComponent) or when computing expensive values that would otherwise recalculate on every render.
They are noise when used for cheap computations, when dependencies change frequently, or when the component rarely re-renders, as the overhead outweighs benefits.
Use profiling tools to measure actual performance impact before adding memoization, and consider alternatives like restructuring components or lifting state.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Start by clarifying that useRef returns a mutable object whose .current property persists across renders without causing re-renders. Then contrast DOM refs (attached to elements via the ref attribute) with instance values (stored in .current for any mutable data). Illustrate with practical examples like storing timers, previous values, or imperative handles.
Pro tip: Emphasize that useRef is not just for DOM—it's a general-purpose 'box' for values that shouldn't trigger re-renders. Mention that overusing it for state can lead to stale UIs, so use it judiciously.
Explain that useRef returns a mutable ref object with a .current property that persists for the full lifetime of the component.
Describe how DOM refs are attached to React elements via the ref attribute to access the underlying DOM node directly.
Explain that you can store any mutable value in .current, such as timers, previous props/state, or third-party library instances, without triggering re-renders.
Contrast DOM refs (read-only after mount, tied to element lifecycle) with instance values (fully mutable, independent of rendering).
Provide examples like storing interval IDs, tracking previous values, or integrating with non-React libraries, and note when to prefer state instead.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Talked about a useDebounce hook and a useOutsideClick hook.
Choose a custom hook that solved a real, recurring problem in your app, and structure your answer around the problem, the abstraction, and the measurable benefits. Emphasize the trade-offs you considered and why the abstraction was worth it, especially in terms of reusability, testability, and maintainability.
Pro tip: Quantify the impact where possible (e.g., reduced code duplication by X%, decreased bug reports by Y%) and mention how you ensured the hook remained flexible without becoming over-abstracted.
Briefly describe the problem or repeated pattern that motivated the custom hook, including the specific pain points (e.g., duplicated logic, complex state management).
Outline the hook's API, its inputs and outputs, and how it encapsulates the logic. Highlight any key decisions like parameterization or return values.
Detail the concrete advantages: reduced duplication, improved readability, easier testing, and consistent behavior across components.
Acknowledge any downsides (e.g., added indirection, potential over-abstraction) and explain why you chose a custom hook over alternatives like HOCs or render props.
Summarize the impact (e.g., time saved, fewer bugs) and what you learned about abstraction, such as when to extract a hook and when to keep logic local.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.
Said useReducer makes sense when state transitions have logic that needs to be explicit and testable, or when next state depends on previous state in complex ways.
Start by defining clear criteria for when useReducer is preferable to useState, such as complex state logic, multiple sub-values, or frequent updates. Then explain how combining useReducer with useContext enables a scalable state management pattern that avoids prop drilling and centralizes dispatch logic. Emphasize that this pattern is not a replacement for Redux but a lightweight alternative for medium-complexity apps.
Pro tip: Mention that useReducer + useContext can cause unnecessary re-renders if the context value changes frequently, and suggest splitting contexts or using memoization to optimize performance. This shows you understand real-world trade-offs beyond just API differences.
List scenarios where useReducer shines: complex state transitions, state objects with multiple fields, next state depends on previous, and when you want to centralize update logic in a reducer function.
Contrast with useState: simpler for independent primitive values, but becomes unwieldy when managing interrelated state or when multiple event handlers need to update the same state in different ways.
Describe how to create a context that provides both the state and the dispatch function from useReducer, allowing deeply nested components to read state and dispatch actions without prop drilling.
Acknowledge that context triggers re-renders in all consumers when the value changes, and suggest optimizations like splitting state and dispatch into separate contexts or using useMemo.
Walk through a brief example, such as a todo app or a multi-step form, where useReducer + useContext simplifies state management compared to useState alone.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.