Skills & Technologies

React Interview Questions & Answers: Hooks, Patterns, and Performance

20 min readUpdated April 12, 2025
Reacthooksstate management
React is the dominant frontend library for building user interfaces, used by companies from startups to Meta, Netflix, and Airbnb. React-specific interviews go beyond general JavaScript knowledge to test your understanding of component architecture, hooks, rendering behavior, and performance optimization. This guide covers the most commonly asked React interview questions across all experience levels. From foundational concepts like the virtual DOM and component lifecycle to advanced topics like custom hooks, memoization strategies, and Next.js server components, each answer is structured to help you demonstrate both theoretical understanding and practical expertise.

React Hooks & Component Patterns

Hooks are the foundation of modern React development. Interviewers test your ability to use them correctly and understand their behavior: • Core hooks — useState, useEffect, useContext, useRef • Performance hooks — useMemo, useCallback, useTransition • Custom hooks — extracting reusable logic, composition patterns • Rules of hooks — why they can't be called conditionally and how the order matters

Q1.Explain the useEffect hook. What are its common pitfalls, and how do you avoid them?

intermediate
useEffect lets you synchronize a component with an external system (API calls, subscriptions, DOM manipulation). It runs after the component renders. Anatomy of useEffect: useEffect(() => { // Effect logic (runs after render) return () => { // Cleanup (runs before next effect or unmount) }; }, [dependencies]); Dependency array behavior: • No array — effect runs after every render (rarely what you want) • Empty array [] — effect runs once after mount (like componentDidMount) • With dependencies [a, b] — effect runs when a or b changes Common pitfalls and fixes: • Missing dependencies — stale closures cause bugs. Always include all referenced values in the dependency array, or use the useRef escape hatch • Object/array dependencies — objects and arrays are compared by reference. Use useMemo to stabilize references, or extract specific primitive values • Infinite loops — setting state inside an effect that depends on that state. Use a functional state updater or restructure the logic • Race conditions in data fetching — use a cleanup flag or AbortController to cancel stale requests Best practice: Each effect should do one thing. Split unrelated effects into separate useEffect calls.

Q2.What are custom hooks? When and how should you create one?

intermediate
A custom hook is a JavaScript function whose name starts with use and that calls other hooks. It lets you extract and reuse stateful logic across components. When to create a custom hook: • When two or more components share the same stateful logic • When a component's hook logic becomes complex enough to warrant its own abstraction • When you want to make logic testable in isolation Example — useFetch: function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const controller = new AbortController(); fetch(url, { signal: controller.signal }) .then(res => res.json()) .then(setData) .catch(setError) .finally(() => setLoading(false)); return () => controller.abort(); }, [url]); return { data, loading, error }; } Design principles for custom hooks: • Return values the consumer needs (not internal implementation details) • Accept configuration through parameters • Handle cleanup properly • Follow the naming convention — always prefix with use Common custom hooks in production: • useDebounce — debounce rapidly changing values • useLocalStorage — sync state with localStorage • useMediaQuery — respond to viewport changes • useIntersectionObserver — lazy loading and infinite scroll

Q3.Explain the difference between controlled and uncontrolled components in React.

beginner
Controlled components: • React state is the single source of truth for the input value • The component controls the value via value prop and onChange handler • Every keystroke triggers a state update and re-render • Ideal for: form validation, conditional disabling, formatting input as the user types Uncontrolled components: • The DOM itself manages the input state • Access the value via a ref (useRef) when needed (e.g., on form submit) • Fewer re-renders — the component doesn't re-render on every keystroke • Ideal for: simple forms, file inputs (<input type="file"> must be uncontrolled), and integrating with non-React libraries When to choose which: • Default to controlled components — they give you more control and are easier to test • Use uncontrolled components when performance matters (high-frequency updates) or when integrating with third-party DOM libraries • React Hook Form uses uncontrolled components internally for performance, while Formik uses controlled components Key rule: Never switch between controlled and uncontrolled during a component's lifetime — React will warn about this. Always decide upfront.

State Management & Data Flow

Understanding how data flows through a React application is critical for building maintainable apps. Key topics: • Component state — useState for local state, lifting state up for shared state • Context API — useContext for avoiding prop drilling, performance considerations • External state libraries — Redux, Zustand, Jotai, and when each is appropriate • Server state — React Query / TanStack Query for caching and synchronizing server data

Q4.When should you use React Context vs a state management library like Redux or Zustand?

advanced
React Context is best for: • Low-frequency updates — theme, locale, auth status • Data that rarely changes and is needed by many components • Simple applications with straightforward state needs Why Context has limitations: • Any context value change re-renders ALL consumers, even if they only use a portion of the value • No built-in memoization of selectors • Complex state logic (multiple interdependent values) becomes unwieldy with Context alone Use a state management library when: • State updates frequently (real-time data, complex forms) • You need fine-grained reactivity (only re-render components that use changed data) • State logic is complex enough to benefit from actions/reducers/middleware • You need devtools for debugging state changes Library comparison: • Redux Toolkit — large ecosystem, middleware, great devtools, more boilerplate • Zustand — minimal API, no providers needed, great for medium complexity • Jotai — atomic state model, fine-grained reactivity, great for derived state • TanStack Query — specifically for server state (caching, refetching, optimistic updates) Practical rule: Start with local state and Context. Add a library only when you hit a real pain point, not preemptively.

React Performance Optimization

Performance optimization questions separate senior candidates from mid-level ones. Interviewers look for understanding of: • Re-render mechanics — what triggers re-renders and how to minimize unnecessary ones • Memoization — React.memo, useMemo, useCallback and when they actually help • Code splitting — React.lazy, Suspense, and dynamic imports • Virtualization — rendering large lists efficiently with react-window or react-virtuoso

Q5.When should you use React.memo, useMemo, and useCallback? When are they unnecessary?

advanced
These are memoization tools that prevent unnecessary computations and re-renders, but they have costs too. React.memo(Component): • Wraps a component to skip re-renders if props haven't changed (shallow comparison) • Use when: a component renders the same output for the same props AND re-renders are expensive • Skip when: the component is cheap to render, or its props change on every render anyway useMemo(() => value, [deps]): • Caches a computed value between re-renders • Use when: the computation is expensive (sorting large arrays, complex calculations) • Skip when: the computation is trivial — useMemo itself has overhead (storing the previous result, comparing dependencies) useCallback(fn, [deps]): • Caches a function reference between re-renders • Use when: passing callbacks to memoized child components (otherwise the new function reference breaks React.memo) • Skip when: the child component isn't memoized — stabilizing the reference has no benefit The golden rule: • Profile first, optimize second. React is fast by default • Premature memoization adds complexity and memory overhead with minimal benefit • Focus on: avoiding state in the wrong place, splitting components at natural boundaries, and virtualizing long lists

Frequently Asked Questions

Should I learn class components for interviews?+

Understand them at a conceptual level (lifecycle methods, this binding) since you'll encounter them in legacy codebases, but focus your preparation on hooks and functional components. No modern React interview expects you to write class components — they may ask you to explain the differences or discuss migration strategies.

How deeply should I know Next.js for React interviews?+

It depends on the role. For general React positions, understanding the concepts (SSR vs SSG vs ISR, file-based routing, API routes) is sufficient. For roles that specifically use Next.js, expect questions on the App Router, React Server Components, server actions, and caching strategies. Next.js knowledge is increasingly expected for senior frontend roles.

What testing approach should I be prepared to discuss?+

Know React Testing Library's philosophy (test behavior, not implementation) and be able to write tests for hooks, async components, and user interactions. Understand the testing pyramid: unit tests for utilities, integration tests for component behavior, and E2E tests (Playwright/Cypress) for critical user flows. Avoid questions about enzyme — it's deprecated.

Ready to land your dream job?

CareerUplift gives you AI-powered mock interviews, an ATS-optimized resume builder, and personalized coaching — everything you need to get hired faster.

Related Articles