React state update not reflected immediately inside useEffect

Summary

A React component derived state from another state variable immediately after calling its setter, causing stale reads and out‑of‑sync UI behavior. Because React batches and defers state updates, the component attempted to filter products before the new value was applied, resulting in filtered always lagging behind.

Root Cause

The issue stems from reading state synchronously after calling its setter, which does not work in React because state updates are asynchronous and batched.

Key factors:

  • setProducts(data) schedules an update but does not immediately change products
  • setFiltered(products.filter(...)) runs using the old products value
  • The effect runs only once ([]), so filtered never recomputes when products changes

Why This Happens in Real Systems

Real React applications frequently hit this problem because:

  • State setters do not update immediately
  • Effects run after render, not during
  • Derived state is often computed too early
  • Developers assume synchronous behavior, especially when coming from other frameworks

Real-World Impact

This pattern can cause:

  • UI showing outdated or empty lists
  • Race conditions when fetching and transforming data
  • Hard‑to‑debug inconsistencies between displayed data and actual state
  • Extra re-renders when trying to “fix” the issue with unnecessary state

Example or Code (if necessary and relevant)

A correct pattern is to derive filtered after products updates, using a separate effect:

import { useEffect, useState } from "react";

export default function Products() {
  const [products, setProducts] = useState([]);
  const [filtered, setFiltered] = useState([]);

  useEffect(() => {
    fetch("/api/products")
      .then(res => res.json())
      .then(data => setProducts(data));
  }, []);

  useEffect(() => {
    setFiltered(products.filter(p => p.inStock));
  }, [products]);

  return (
    
    {filtered.map(p => (
  • {p.name}
  • ))}
); }

How Senior Engineers Fix It

Experienced engineers avoid stale state by:

  • Deriving state inside a dedicated effect that depends on the source state
  • Avoiding redundant state entirely when possible
  • Using memoization (useMemo) for derived values
  • Keeping state minimal and single‑sourced

Common senior patterns:

  • Compute derived values from state, not store them
  • Use effects only when side effects are required
  • Treat state setters as asynchronous signals, not immediate mutations

Why Juniors Miss It

Less experienced developers often:

  • Assume setState works like a synchronous variable assignment
  • Don’t yet understand React’s render cycle
  • Forget that effects run after render, not during
  • Try to store derived values instead of computing them
  • Don’t recognize that duplicated state leads to drift

The core misunderstanding is believing that React updates state immediately, when in reality it schedules updates and re-renders later.

Leave a Comment