Top 5 React Interview Mistakes in 2026

·11 min read
By ·Updated
reactinterview-questionsinterview-tipsfrontendcareerhooks

React interview mistakes often come from describing a fix without the model that makes it correct. “Add the dependency,” “use a functional setter,” and “never use an index key” are shortcuts. A strong React 19.2 answer explains synchronization, snapshots, purity, and identity first.

React Compiler 1.0 is stable and can apply automatic memoization when a project enables it. That makes cargo-cult memo, useMemo, and useCallback answers even less convincing: optimization must follow a measured problem and the actual compiler/runtime configuration.

Table of Contents

  1. Using Effects for internal data flow
  2. Confusing render with a DOM update
  3. Calling state setters asynchronous
  4. Reciting an outdated Rules of Hooks slogan
  5. Treating keys as a performance-only prop

Mistake 1: Using Effects for Internal Data Flow

Why is an Effect not a general “after render” callback?

An Effect synchronizes a component with an external system: a network connection, browser subscription, timer, non-React widget, or similar resource. If a value can be derived from props and state during render, an Effect plus extra state creates an unnecessary render and another consistency boundary.

// Avoid: derived state synchronized by an Effect.
function SearchResults({items, query}) {
  const [visible, setVisible] = useState([]);
 
  useEffect(() => {
    setVisible(filterItems(items, query));
  }, [items, query]);
 
  return <List items={visible} />;
}
 
// Prefer: derive during render.
function SearchResults({items, query}) {
  const visible = filterItems(items, query);
  return <List items={visible} />;
}

Event-specific work belongs in the event that knows what happened:

function Checkout({cart}) {
  async function handleSubmit() {
    await placeOrder(cart);
    showToast('Order placed');
  }
 
  return <button onClick={handleSubmit}>Place order</button>;
}

An external subscription does need an Effect and symmetric cleanup:

function ChatRoom({roomId}) {
  useEffect(() => {
    const connection = createConnection(roomId);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);
 
  return <Chat roomId={roomId} />;
}

Dependencies are not a schedule you tune manually. They describe all reactive values read by setup. React compares them with Object.is. If a dependency causes unwanted re-synchronization, change the code:

  • move event logic to an event handler;
  • derive render data during render;
  • move constants outside the component;
  • create an object or function inside the Effect when it exists only there;
  • use an updater to avoid reading state only to calculate next state;
  • in React 19.2, use useEffectEvent for genuinely non-reactive logic called from an Effect.

An Effect Event is not a dependency escape hatch. It can be called only from Effects or other Effect Events, intentionally has non-stable identity, must not be listed as a dependency, and should not hide a value that really must re-synchronize the external resource.

Data fetching also needs race and ownership handling. Cleanup can abort an owned request or ignore an obsolete result, but a framework/router data API may provide better server rendering, caching, deduplication, and waterfall control than ad hoc fetch Effects.


Mistake 2: Confusing Render with a DOM Update

What does a React re-render actually mean?

Rendering means React calls components to calculate a tree. Committing is when React applies necessary host changes and runs layout/passive work in their respective phases. A component function running does not imply that every DOM node is destroyed or changed.

function App() {
  const [count, setCount] = useState(0);
 
  return (
    <>
      <button onClick={() => setCount(value => value + 1)}>
        {count}
      </button>
      <Report />
    </>
  );
}

After App state changes, React normally evaluates the child tree. Reconciliation can preserve Report's DOM if its output is unchanged. Whether evaluating Report matters depends on actual work, props, context, identity, external stores, Suspense, and optimization.

Avoid three common overclaims:

  1. “Every state change re-renders the whole app.” State schedules work for the owning component; ancestors and unrelated branches are not automatically invoked.
  2. “A re-render redraws the DOM.” React calculates and then commits only required host mutations.
  3. React.memo means the child never renders.” Local state, consumed context, changed props, remounts, or cache invalidation can still render it; memoization is an optimization, not a semantic guarantee.

First improve state placement and component boundaries. Then profile. If the project uses React Compiler, it can automatically reuse component work and calculations that manual memo or useMemo previously guarded. Manual memoization still has uses—for example, an API contract requiring stable identity or a measured hotspot—but it must never be required for correctness.

In development Strict Mode, React can intentionally call render logic and Effect setup/cleanup extra times to expose impurities and missing cleanup. Do not “fix” this by disabling correctness checks; make render pure and cleanup symmetric.


Mistake 3: Calling State Setters Asynchronous

Why do three direct increments often produce one increment?

Each render sees a snapshot of state. Calling a setter queues an update; it does not mutate the variable in the already-running handler and it does not return a Promise.

function Counter() {
  const [count, setCount] = useState(0);
 
  function addThreeWrong() {
    setCount(count + 1);
    setCount(count + 1);
    setCount(count + 1);
    console.log(count); // Current render's snapshot.
  }
 
  function addThree() {
    setCount(value => value + 1);
    setCount(value => value + 1);
    setCount(value => value + 1);
  }
 
  return <button onClick={addThree}>{count}</button>;
}

The direct calls each queue replacement based on the same snapshot. Updater functions are processed in order against pending state.

Use a direct value when next state does not depend on pending state:

setStatus('submitted');

Use an updater when it does:

setItems(items => [...items, newItem]);

Functional updaters do not make mutation safe:

// Wrong: returns the same mutated object.
setUser(user => {
  user.name = 'Alicia';
  return user;
});
 
// Correct immutable replacement.
setUser(user => ({...user, name: 'Alicia'}));

useState replaces its value; it does not shallow-merge objects like class this.setState. React can ignore an update whose next state is Object.is-equal to current state. Batching is an implementation strategy around update processing, not a promise that setters are JavaScript Promises. If later behavior belongs to the same user action, keep it in the event handler; use an Effect only when an external system must synchronize with committed state.


Mistake 4: Reciting an Outdated Rules of Hooks Slogan

Can anything named use run conditionally?

Normal Hooks must be called at the top level of a function component or custom Hook, before conditional returns. React associates Hook state with call order.

function Profile({userId}) {
  const [user, setUser] = useState(null);
 
  useEffect(() => {
    if (!userId) return;
    // Synchronize or delegate to a data layer.
  }, [userId]);
 
  if (!userId) return <EmptyState />;
  return <ProfileView user={user} />;
}

Do not call useState, useEffect, or useContext in conditions, loops, nested callbacks, event handlers, async functions, class methods, or after a conditional return. Use the current eslint-plugin-react-hooks preset; its rules also help React Compiler understand safe code.

React 19.2's use API is the important exception. Despite its name, it is not a Hook and may read a Promise or context conditionally or in a loop:

function Message({shouldShow, messagePromise}) {
  if (!shouldShow) return null;
  const message = use(messagePromise);
  return <p>{message}</p>;
}

use still must run inside a component or Hook and cannot be wrapped in try/catch; use Suspense and an Error Boundary for a Promise. A Promise passed from a Server Component to a Client Component must meet the framework's caching and serialization contract.

The deeper rule is purity. Components and Hooks must be idempotent for the same inputs, avoid side effects during render, and not mutate values they did not create. Hook order is only one part of the Rules of React.


Mistake 5: Treating Keys as a Performance-Only Prop

What do keys control?

Keys identify siblings across renders. React uses element type, position, and key to decide whether state belongs to the same component identity or a new one.

function TodoList({todos}) {
  return (
    <ul>
      {todos.map(todo => (
        <TodoRow key={todo.id} todo={todo} />
      ))}
    </ul>
  );
}

If a dynamic list uses its current index, inserting, removing, or sorting can associate existing component state with the wrong data item. The main problem is identity—not merely “React re-renders everything.”

Key rules:

  • unique among siblings, not globally;
  • stable for the same data identity;
  • stored or derived from data, not generated during render;
  • not passed to the component as a normal prop;
  • index can be sufficient only for permanently static order/membership where item state need not follow a distinct identity;
  • Math.random() creates a new identity each render and therefore remounts.

Changing a key can be intentional:

<Editor key={documentId} documentId={documentId} />

When documentId changes, React resets Editor and its local state. Use that when state belongs to one document; do not use a random key as a workaround for incorrect synchronization.

Keys apply outside list syntax too. State is tied to a position in the rendered tree, and a key lets you distinguish two conceptual components rendered at the same position. This identity model is the answer interviewers need.


Quick Reference

Weak answerStronger React 19.2 answer
“Add/remove dependencies until the Effect runs when I want.”“Effects synchronize external systems; dependencies follow reactive reads.”
“A parent update redraws every child.”“Render calculates a tree; commit applies required host changes; profile actual work.”
“Setters are async, so use a timeout or await them.”“State is a render snapshot; setters queue replacements or updater functions.”
“No use* call can be conditional.”“Normal Hooks require top-level order; the use API is a documented exception.”
“Index keys are slow.”“Keys encode sibling identity and determine state preservation or reset.”

Frequently Asked Questions

What is the biggest React interview mistake in 2026?

The biggest mistake is treating Effects, memoization, or keys as recipes instead of parts of React's model. Start from render purity, state snapshots, identity in the tree, and synchronization with an external system. Then explain what happens during render and commit, which value an Effect synchronizes, and what evidence justifies an optimization.

Does every value used by useEffect belong in its dependency array?

Every reactive value read by the Effect setup belongs in its dependencies unless the code is restructured so it is no longer reactive Effect logic. Dependencies are determined by code, not chosen to control frequency. Move event-specific work to an event handler, derive render data during render, move stable values outside the component, or use React 19.2 useEffectEvent only for genuinely non-reactive logic called from an Effect.

Are React state updates asynchronous?

It is more precise to say that a setter queues work for another render and the current render's state variable is a snapshot that does not change. React batches updates at defined boundaries. Pass a value to replace state based on the current snapshot, or pass an updater when the next state depends on pending state. A setter does not return a Promise you can await.

Does a parent render always re-render every child?

React normally evaluates the returned child tree after a parent renders, but rendering is not the same as committing DOM changes. React can preserve host nodes during reconciliation, bail out on unchanged state, reuse memoized work, and apply React Compiler optimizations when enabled. Context, state, props, identity, Suspense, and external stores also matter. Profile before claiming a render is expensive.

Can React use be called conditionally?

Yes. Despite its name, the React 19.2 use API is not a Hook and can be called in loops and conditions, though it must run inside a component or Hook and cannot be wrapped in try catch. Normal Hooks such as useState, useEffect, and useContext must remain at the top level before conditional returns. The current eslint-plugin-react-hooks rules encode these distinctions.

When is an array index an acceptable React key?

An index can be adequate when sibling order and membership are permanently static and item state never needs to follow a data identity. For dynamic lists, use a stable identifier stored in the data and unique among siblings. Keys are identity, not a generic performance hint: a changed key intentionally resets component state, while a random key remounts on every render.

Sources


Ready to ace your interview?

Get 550+ interview questions with detailed answers in our comprehensive PDF guides.

View PDF Guides