obah sylva

Common React Mistakes Beginners Make (And How to Fix Them)

Share this article

On December 3, 2025, the React team disclosed CVE-2025-55182, a critical remote-code-execution vulnerability in React Server Components rated a full CVSS 10.0 — severe enough that a single crafted HTTP request could hand an attacker complete control of an unpatched server. It’s an extreme example, but it makes a point that applies just as much to a beginner’s first component as it does to a production RSC app: in React, the mistakes that look small in the moment — a missing dependency, a directly mutated state object, an unpatched package — are usually the ones that cost the most later. Here are the mistakes that trip up React developers most often in 2026, and exactly how to fix each one.

Mistake 1: Treating the useEffect dependency array as optional

This is still the single most common bug in beginner React hooks code. Leave the dependency array off entirely and the effect reruns after every render. Fill it with the wrong values and the effect either runs too often or holds onto stale data from a previous render.

// ❌ Runs on every single render — no dependency array
useEffect(() => {
  fetchUser(userId);
});

// ❌ Missing a real dependency — stale userId if it changes
useEffect(() => {
  fetchUser(userId);
}, []);

// ✅ Runs only when userId actually changes
useEffect(() => {
  fetchUser(userId);
}, [userId]);

The fix is rarely to silence the ESLint warning for react-hooks/exhaustive-deps. It’s to actually list every value the effect reads. If that list keeps growing, that’s usually a sign the logic belongs somewhere else entirely, which leads to the next mistake.

Mistake 2: Mutating state directly instead of replacing it

React decides whether to re-render by comparing references, not by deeply inspecting objects. Push onto an array or set a property on a state object directly, and React often can’t tell anything changed — so the UI silently doesn’t update.

// ❌ Mutates the existing array — same reference, no re-render
todos.push(newTodo);
setTodos(todos);

// ✅ Creates a new array — React sees a new reference
setTodos([...todos, newTodo]);

// ❌ Mutates a nested object property directly
user.name = 'New Name';
setUser(user);

// ✅ Spreads into a new object
setUser({ ...user, name: 'New Name' });

This is exactly the kind of bug that passes a quick manual test and then reappears in production three weeks later when a list stops updating for one specific user.

Mistake 3: Using the array index as a list key

It compiles, it renders, and it looks fine — until the list can be reordered, filtered, or have an item removed from the middle. At that point React matches the wrong internal state to the wrong row, and inputs, checkboxes, or animations attach to the wrong item.

// ❌ Breaks if items are reordered or removed
{items.map((item, index) => (
  
))}

// ✅ Stable identity tied to the data itself
{items.map((item) => (
  
))}

The fix costs nothing: use a stable, unique ID from the data. If the data genuinely has no ID, generate one when the item is created — not from its position in the array.

Mistake 4: Reaching for useEffect when you don’t need it

Not every derived value needs a state variable plus an effect to keep it in sync. If a value can be calculated directly from props or existing state during render, calculating it during render is simpler, faster, and has zero risk of getting out of sync.

// ❌ Unnecessary state + effect just to derive a value
const [fullName, setFullName] = useState('');
useEffect(() => {
  setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);

// ✅ Just calculate it during render
const fullName = `${firstName} ${lastName}`;

Reserve useEffect for genuine side effects — subscriptions, manual DOM work, and syncing with something outside React — not for computing values React can already compute for you.

Mistake 5: Prop drilling instead of composition or Context

Passing a prop through four or five components that don’t use it, just so a deeply nested child can read it, makes every one of those intermediate components harder to reuse and refactor. For state that’s genuinely global to a section of the tree — the current user, a theme, a locale — the Context API exists precisely to skip the drilling. For everything else, restructuring components so children are passed in as children or composed directly often removes the need for the prop chain entirely.

Mistake 6: Ignoring dependency and security updates

This is the mistake that made CVE-2025-55182 so damaging. The flaw lived in how React Server Components deserialized incoming request payloads, and it affected React 19.0, 19.1.0, 19.1.1, and 19.2.0. The React team shipped fixes within days — in versions 19.0.1, 19.1.2, and 19.2.1 — but Microsoft, Google, and Palo Alto Networks all separately reported active exploitation in the wild within 48 hours of disclosure, hitting organizations that hadn’t yet updated.

All users should upgrade to the latest patched version in their release line.

React team, react.dev security advisory, December 3, 2025

The lesson isn’t specific to this one CVE. It’s that treating npm outdated as a routine, low-priority chore is itself a mistake — the same category as a missing dependency array, just with a much bigger blast radius. Keep React, Next.js, and their related packages current, and read release notes when a security advisory lands, not months later.

A quick checklist

  • List every value your effect reads in its dependency array, or restructure so it doesn’t need to.
  • Never mutate state directly — always create a new array or object.
  • Key lists with a stable ID from the data, never the array index.
  • Calculate derived values during render instead of syncing them with an effect.
  • Reach for Context or composition before drilling a prop through five components.
  • Patch React and its framework on top — like Next.js — as soon as a security advisory ships.

None of these mistakes require advanced React knowledge to fix — they require knowing where to look. Get these six right and most of the confusing, hard-to-reproduce bugs that make beginners doubt themselves simply stop happening.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top