August 13, 2025

React Common Mistakes and How to Avoid Them

When learning React, it’s normal to make small mistakes that lead to bugs or unexpected behavior. I’ve summarized my own learning experience into a handy React “Error Notebook” to help you avoid the same pitfalls.


1. Mixing JSX and JavaScript: Don’t call functions directly

Wrong:

<input type="text" onChange={handleInputChange()} />
<button onClick={addTodo(inputValue)}>Add</button>

Problem:
Calling the function directly (with ()) executes it immediately, rather than waiting for the event. Also, handleInputChange() needs the event object, which isn’t passed in this way, causing errors like Cannot read property 'target' of undefined.

Correct:

<input type="text" onChange={handleInputChange} />
<button onClick={() => addTodo(inputValue)}>Add</button>

Explanation:

  • onChange={handleInputChange} passes the function reference. React automatically passes the event object.
  • onClick={() => addTodo(inputValue)} uses an arrow function to delay execution until the click event.

Summary

No arguments → just pass the function name:

onClick={sayHi}

With arguments → wrap in an arrow function:

onClick={() => sayHi("Jigang")}

2. Updating Arrays or Objects in state

Wrong:

tasks[index].completed = !tasks[index].completed;
setTasks(tasks); // ❌ React may not detect changes

Problem:
React state should not be mutated directly; otherwise React might not re-render the component.

Correct:

setTasks(tasks.map((task, i) =>
  i === index ? { ...task, completed: !task.completed } : task
));

Explanation:

  • { ...task } creates a new object, avoiding mutation of the original.
  • map returns a new array, triggering re-render.

3. Deleting Array Elements

Wrong:

setTasks([...tasks.filter((task, index) => index !== i)]);

Problem:
Using the spread operator ... is unnecessary here; filter already returns a new array.

Correct:

setTasks(tasks.filter((task, index) => index !== i));

4. Passing Functions via props

Wrong:

<Taskitem task={task} onDelete={delTask(index)} />

Problem:
This calls the function immediately, instead of waiting for the click event.

Correct:

<Taskitem task={task} onDelete={() => delTask(index)} />

Use an arrow function to defer execution.


5. Conditional Rendering and Optional Chaining

Wrong:

<h2>{user[0].name.first}</h2>
<img src={user[0].picture.medium} />

Problem:
user[0] may not exist yet (data not loaded), causing Cannot read property 'name' of undefined.

Correct (logical AND):

{user.length > 0 && (
  <>
    <h2>{user[0].name.first} {user[0].name.last}</h2>
    <img src={user[0].picture.medium} />
  </>
)}

Correct (optional chaining):

<h2>{user[0]?.name?.first} {user[0]?.name?.last}</h2>
<img src={user[0]?.picture?.medium} />

?. allows safe access to nested properties, preventing runtime errors.


6. Async functions in useEffect

Wrong:

useEffect(async () => {
  const res = await fetch(url);
}, []);

Problem:
useEffect cannot directly accept an async function (it returns a Promise).

Correct:

useEffect(() => {
  async function fetchData() {
    const res = await fetch(url);
  }
  fetchData();
}, []);

7. Controlled Input Components

Wrong:

<input type="text" value={inputValue} onChange={handleInputChange()} />

Problem:
Calling handleInputChange() immediately prevents React from passing the event object.

Correct:

<input type="text" value={inputValue} onChange={handleInputChange} />

8. Mixing multiple JSX elements in conditional rendering

Wrong:

{user.length > 0 &&
  <h2>{user[0].name.first}</h2>
  <p>{user[0].location.city}</p>
}

Problem:
JSX must return a single expression. Multi-line JSX requires wrapping with parentheses or a Fragment.

Correct:

{user.length > 0 && (
  <>
    <h2>{user[0].name.first}</h2>
    <p>{user[0].location.city}</p>
  </>
)}

Key Takeaways

  1. Wrap JavaScript expressions in {} inside JSX.
  2. Don’t call functions directly in JSX events; use arrow functions if arguments are needed.
  3. Always create new objects/arrays when updating state.
  4. Use () or <></> when rendering multiple elements conditionally.
  5. Optional chaining ?. prevents undefined errors when accessing nested properties.
  6. Async operations should be wrapped inside useEffect.
  7. Controlled input components must use value + onChange, with the event object properly handled.