SkillLynk Skill Lynk connect skills with opportunities
Menu

React Interview Questions & Answers

React interview questions covering components, hooks, state management, and performance -- everything a frontend interview panel expects a React developer to know.

18 Questions ~27 min read Beginner: 3 Intermediate: 10 Advanced: 5

Coding 5

Props are read-only data passed from a parent component; state is data owned and managed inside a component that can change over time and triggers a re-render when updated.

Detailed Answer

Props flow one-way, from parent to child, and a component should never modify its own props -- if it needs different data, the parent passes new props. State is local to a component (or lifted up to a shared ancestor) and is updated via setState/useState, which schedules a re-render with the new value. A component can be purely presentational, driven entirely by props, or stateful, managing its own internal data.
componentsstate-management
useState holds a piece of local component state across re-renders; useEffect runs side effects (data fetching, subscriptions, DOM manipulation) after render, optionally re-running when its dependencies change.

Detailed Answer

`const [count, setCount] = useState(0)` gives you a value and a setter that triggers a re-render when called. `useEffect(() => { fetchData(); }, [id])` runs the fetch after the component mounts and again whenever `id` changes, because it's listed in the dependency array. An empty dependency array (`[]`) means the effect runs only once, on mount; omitting the array entirely means it runs after every render.

Best Practices

Always include every value the effect reads from component scope in the dependency array (or use a linter rule like exhaustive-deps) to avoid stale closures.

Common Mistakes

Omitting a value from useEffect's dependency array to 'stop it from re-running,' which causes the effect to silently use a stale, captured version of that value instead.
hooksstate-management
Use JavaScript expressions directly in JSX -- ternaries, && short-circuiting, or early returns -- rather than a template-specific directive.

Detailed Answer

Because JSX is just JavaScript, you can write `{isLoggedIn ? : }` for either/or rendering, `{items.length > 0 && }` to render something only when a condition is true, or return early from the component function entirely (`if (loading) return ;`) before the main render output.

Best Practices

Be careful with && for numeric conditions -- `{count && }` renders a literal 0 on the page when count is 0, since 0 is falsy but still gets rendered as text; use `{count > 0 && ...}` instead.

Common Mistakes

Writing `{count && }` and seeing a stray '0' rendered on the page when count is zero.
components
Trigger the fetch inside useEffect on mount (or when a dependency changes), track loading/data/error in state, and render conditionally based on which state is active.

Detailed Answer

A typical pattern: `const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null);` then inside `useEffect`, call the API, and set data/loading/error accordingly, including in a try/catch so failures update the error state instead of throwing unhandled. Cleanup (e.g. an AbortController) matters if the component can unmount or the dependency can change before the request finishes.

Best Practices

Guard against setting state after the component has unmounted (a stale request completing late) by using a cleanup function or an AbortController.

Common Mistakes

Fetching data without a cleanup/cancellation mechanism, causing a 'setState on an unmounted component' warning or, worse, a stale response overwriting newer data.
hooksstate-management
JSX is a syntax extension that looks like HTML inside JavaScript; a compiler (Babel) transforms it into plain React.createElement() calls (or the newer automatic JSX runtime) before it reaches the browser.

Detailed Answer

Browsers don't understand JSX natively -- `
{title}
` is compiled into something like `React.createElement('div', {className: 'card'}, title)`, which returns a plain JavaScript object describing that element. This is why JSX requires a build step (Babel, or a bundler with a JSX transform) and why you can embed any JavaScript expression inside curly braces -- it's ultimately just function calls, not a templating language with its own separate rules.
components

Conceptual 8

The Virtual DOM is an in-memory representation of the real DOM; React diffs it against the previous version and applies only the minimal set of real DOM updates needed.

Detailed Answer

Directly manipulating the browser DOM is comparatively slow, especially with frequent updates. React keeps a lightweight virtual representation of the UI tree, and whenever state changes, it builds a new virtual tree, diffs it against the previous one (reconciliation), and applies only the actual changes to the real DOM in a batched update -- avoiding unnecessary, expensive re-renders of unaffected elements.
virtual-domperformance
Hooks let function components use state and other React features (previously only available in class components) without writing a class.

Detailed Answer

Before hooks, stateful logic required class components with lifecycle methods (componentDidMount, componentDidUpdate, etc.), and reusing stateful logic across components meant patterns like higher-order components or render props, which added wrapper layers and made component trees harder to follow. Hooks like useState, useEffect, and custom hooks let you extract and reuse stateful logic directly as functions, keeping components flatter and logic more composable.

Common Mistakes

Calling hooks conditionally or inside loops, which breaks React's assumption that hooks are called in the same order on every render.
hooks
A controlled component's value is driven entirely by React state (via value + onChange); an uncontrolled component keeps its own internal DOM state, read via a ref when needed.

Detailed Answer

With a controlled input, every keystroke updates state via onChange, and the input's displayed value always comes from that state -- giving React full visibility and control over the form data at all times. An uncontrolled input just lets the browser manage its own value internally, and you read it on demand with a ref (e.g. on form submit), which is simpler for basic cases but harder to validate or react to in real time.

Best Practices

Use controlled components when you need real-time validation, conditional rendering based on input, or to synchronize the value elsewhere; uncontrolled components are fine for simple, one-shot forms.
componentsstate-management
Context lets you share a value across a component tree without manually passing props through every intermediate level; use it for broadly-needed data like theme, auth, or locale.

Detailed Answer

Prop drilling means passing a prop down through several layers of components that don't themselves use it, just to get it to a deeply nested child. Context (createContext + a Provider) lets any descendant read the value directly via useContext, skipping the intermediate layers. It's well suited to genuinely global, rarely-changing data; for frequently-changing state shared across many components, a dedicated state management library often scales better, since every context consumer re-renders on any context value change.

Best Practices

Split contexts by concern (e.g. separate ThemeContext and AuthContext) rather than one giant context object, so unrelated consumers don't re-render on unrelated changes.

Common Mistakes

Putting frequently-changing state (like every keystroke of a search box) into a single broad context that many components consume, causing widespread unnecessary re-renders.
context-apistate-management
Both manage local state; useReducer centralizes update logic in a single reducer function driven by dispatched actions, which scales better for complex or interrelated state transitions.

Detailed Answer

useState is simplest for independent, simple values. useReducer is a better fit when several pieces of state update together in response to the same action, when the next state depends heavily on the previous state in non-trivial ways, or when you want the update logic testable in isolation from the component (a pure reducer function is easy to unit test without rendering anything).
hooksstate-management
React Router intercepts navigation and swaps rendered components based on the URL without a full page reload, updating the browser's history API to keep the URL and back/forward behavior correct.

Detailed Answer

Instead of the browser requesting a new HTML document from the server on every link click, React Router listens for navigation, prevents the default full-page reload, updates the URL via the History API, and re-renders whichever route component matches the new path -- all within the same loaded page. This gives an app-like feel (instant transitions) but means you need to handle things like scroll restoration, code-splitting per route, and SEO (since content is rendered client-side) deliberately.
routing
Server Components render on the server and send serialized output (not JavaScript) to the client, reducing bundle size and enabling direct backend data access without an API round-trip, while Client Components render (and can be interactive) in the browser as usual.

Detailed Answer

A Server Component can, for example, query a database directly during server rendering and never ships its own logic to the browser, shrinking the client bundle and improving initial load. Client Components are still needed for anything interactive (state, effects, event handlers) and are marked explicitly (e.g. a 'use client' directive in frameworks that support this model). The two compose together: Server Components can render Client Components as children, but not the other way around directly.
performancecomponents
Use a testing library like React Testing Library to render the component and assert on what the user would see/do, rather than testing internal implementation details.

Detailed Answer

React Testing Library encourages querying the rendered output the way a user would (by visible text, label, or role) and firing real events (clicks, typing) rather than reaching into component internals or state directly. This makes tests resilient to refactors that don't change user-visible behavior, and catches regressions that actually matter to users.

Best Practices

Test behavior and output, not implementation detail (e.g. don't assert on a component's internal state variable directly; assert on what's rendered).

Common Mistakes

Writing tests that assert on internal state or call internal methods directly, which breaks on harmless refactors and doesn't actually verify user-facing behavior.
testing

Architecture 1

React diffs the new virtual DOM tree against the previous one element-by-element, using each element's type and, within lists, its key, to decide whether to update, replace, or reorder nodes.

Detailed Answer

When element types differ (e.g. a
becomes a ), React tears down the old subtree and builds a new one. When types match, React updates only the changed attributes/children in place. For lists, the `key` prop tells React which array item corresponds to which rendered element across re-renders -- without stable keys (or using array index as key when items can reorder), React can misattribute state to the wrong item or re-render more than necessary.

Best Practices

Use a stable, unique id as the key for list items, not the array index, especially when the list can be reordered, filtered, or have items inserted/removed.

Common Mistakes

Using array index as key for a list that can be reordered or filtered, which causes component state (like an input's value) to attach to the wrong row after the list changes.
virtual-domperformance

Performance 2

React.memo memoizes an entire component (skipping re-render if props are shallow-equal); useMemo memoizes a computed value; useCallback memoizes a function reference.

Detailed Answer

React.memo wraps a component so React skips re-rendering it if its props haven't shallow-changed since the last render. useMemo caches the result of an expensive calculation between renders, recomputing it only when its dependencies change. useCallback caches a function's identity between renders, which matters when that function is passed as a prop to a React.memo-wrapped child -- without it, a new function reference on every render would defeat the memoization.

Best Practices

Only reach for these when profiling shows an actual unnecessary re-render or expensive recomputation -- they add complexity and, used incorrectly, can make performance worse.

Common Mistakes

Wrapping every component in React.memo and every function in useCallback by default, which adds comparison overhead everywhere without measurable benefit, and can even hurt performance.
performancehooks
Common causes are new object/array/function references created on every render being passed as props, context value changes triggering all consumers, and missing memoization on expensive child components.

Detailed Answer

Use the React DevTools Profiler to record a render pass and see which components re-rendered and why. Frequent culprits: passing a new inline object or arrow function as a prop every render (defeating React.memo on the child), a context value that's a new object literal each render (so every consumer re-renders even if the meaningful data didn't change), and state updates placed too high in the tree, causing large subtrees to re-render for a small, localized change.

Best Practices

Keep frequently-changing state as local as possible in the tree, and memoize object/array/function props that are passed to memoized children.
performancevirtual-dom

Behavioral 1

A strong answer identifies the specific pain (too much responsibility in one component, tangled state, duplicated logic), extracts smaller pieces (custom hooks, child components) incrementally, and verifies behavior didn't regress along the way.

Detailed Answer

Interviewers want to hear a concrete before/after: what made the component hard to work with (e.g. 15 useState calls, deeply nested conditional JSX, mixed data-fetching and presentation logic), how it was broken apart (extracting a custom hook for the data logic, splitting UI into smaller focused components), and how correctness was verified during the refactor (existing tests, manual QA, or adding tests first).
componentsstate-management

Scenario-Based 1

Separate the presentational rendering from the data/state logic (often via a custom hook or a headless-table library pattern), so the same table shell can be reused with different data and column configurations.

Detailed Answer

A common design: the table's state (sort column/direction, filter values, current page) lives in a custom hook or the parent, while the table component itself is a mostly-presentational renderer that receives already-sorted/filtered/paginated rows plus column definitions as props, and emits callbacks (onSortChange, onPageChange) rather than owning the logic itself. This separation makes the table reusable across very different datasets without duplicating sorting/filtering code per usage.

Best Practices

Keep the table component headless/presentational and push data-shaping logic (sort, filter, paginate) into a hook or the parent, so the component stays reusable across contexts.
componentsstate-management
No questions match your filters.

Related Skills

Continue Your Career Journey

Explore on SkillLynk

Sign in required

Sign in