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