Common Mistakes in React and How to Avoid Them

Paul W.
OVRSEA
Published in
4 min readOct 18, 2018

At Ovrsea, when new trainees arrive, they struggle with small but time consuming mistakes when they start React. That’s why I’ll cover here those most common mistakes in order to avoid you possible future headaches.

TL;DR: You have…

  • …a render issue, for example an infinite loop, you may have forgotten arrows in a callback (point 4).
  • …a function that is triggered whereas it shouldn’t, check also your callbacks (point 4).
  • …a state variable that doesn’t change properly, your State variables may be mutated (point 2).
  • …a list of components that doesn’t update well, you probably did not set your keys (point 1).
  • … a component that doesn’t update after state changes, maybe you didn’t know that setState is async ? (point 3).

1. You forgot ‘key’ on a Components list

When you have an array of values and you want to render a component for each of them, you will probably do something like this:

const arr = ['yellow', 'red', 'green'];render() {
return (
<ul>
{arr.map(color =>
<li>{color}</li>)}
</ul>
);
}

This will work fine if the array never changes but if you have to make some modifications in it, for example delete or modify a value, you will probably have some render issues.

Why green still here ? 🤔

This problem can easily be solved by adding a key to the component inside the loop. This will help React to watch what element has changed.

<p key={color}>
{color}
</p>

But be careful, keys must be unique !

You don’t want to use your array index, because it will be reassign if the array order or size changes. In this example we can only use color as key but if the same color is containing twice in the array this will cause unexpected behavior…

If you map an array of objects with IDs or any unique property then you can use it, else you may still use index but only at last resort.

2. You mutated a State variable

Imagine that we have an array of color in our state:

state = {
arr: ['red', 'yellow'],
};

We want to change the color of the first element into green, so we write a function that modify state:

changeColorToGreen = () => {
const arr = this.state.arr;

arr[0] = 'green';

this.setState({ arr });
};

This will correctly change our state but we may have some unexpected behavior by doing it this way. React is looking for object reference changes in memory when it decides to rerender or not, in that case the object is still the same in memory and only it’s content has changed. So components using the array as props may not rerender properly whenever colors change.

changeColorToGreen = () => {
const arr = this.state.arr.slice();
// .slice() return a shallow copy of the array
// We can also use ES6 spread operator:
// const arr = [...this.state.arr]
arr[0] = 'green';

this.setState({ arr });
};

This time our array is well reassigned and its reference in memory has changed, we keep our state immutable.

3. You forgot that setState is asynchronous

When you have to change a value in state, you have to use setState function as we saw in previous examples. But if we try to use setState many times in the same function, we can have some unexpected behavior.

state = {
counter: 1,
};

incrementCounter = () => {
this.setState({ counter: this.state.counter + 1 });
this.setState({ counter: this.state.counter + 1 });
this.setState({ counter: this.state.counter + 1 });
};

In this example, we expect counter to be 4. But as setState is async, it will have no enough time to change the value when the next call is done. So, at the third call, this.state.counter is still at 1 and the result will be only 2.

Each call to incrementCounter will increment counter by 1 instead of 3.

Try it out on CodePen.

4. You forgot arrows in a callback

When beginners have to deal with arrow functions for the first times, they often misunderstanding their syntax. See belove the most common mistake they usually make:

state = {
counter: 1,
};

incrementCounter = (amount) => {
this.setState({ counter: this.state.counter + amount });
};

render() {
return (
<div>
<p>{this.state.counter}</p>
<button onClick={this.incrementCounter(5)} />
</div>
);
}

In this example, we have a button that call incrementCounter function when onClick is triggered. Problem is that it’s the return of incrementCounter function that is assign to onClick, not the function itself. So incrementCounter will be triggered as soon as the component will render.

The correct way to assign a function at events is to use arrow function:

onClick={() => this.incrementCounter(5)}

When you don’t need parameter in your function or parameters are given directly by the event, you can simply write it like this:

onClick={this.myFunction}// Is the same as onClick={(e) => this.myFunction(e)}

For further reading

--

--