How to NOT React: Common Anti-Patterns and Gotchas in React
What is an anti-pattern? Anti-patterns are certain patterns in software development that are considered bad programming practices. The same pattern may have been considered correct at one point in the past, but now developers have realised that they cause more pain and hard-to-track bugs in long term.
React has matured as an UI library and with that a lot of best development practices have evolved over the years. We are going to learn from the collective wisdom of thousands of programmers and developers who learnt those things the hard way.

Let’s begin!
1. bind() and arrow functions in Components
You must have bound your custom functions in the constructor
function before using them as props for components. If you declare components using the extends
keyword, then the custom functions (such as updateValue
below) lose their this
bindings. So, if you want to access this.state
, or this.props
or this.setState
then you need to re-bind them.
Demo
class app extends Component {
constructor(props) {
super(props);
this.state = {
name: ''
};
this.updateValue = this.updateValue.bind(this);
}updateValue(evt) {
this.setState({
name: evt.target.value
});
}render() {
return (
<form>
<input onChange={this.updateValue} value={this.state.name} />
</form>
)
}
}
Problems
There are two ways to bind the custom functions to the component’s this
. One way is to bind them in the constructor
as done above. The other way is to bind at the time of passing as prop value —
<input onChange={this.updateValue.bind(this)} value={this.state.name} />
This method suffers from a problem. Since .bind()
creates a new function each time it is run, this method would lead to a new function being created every time the render
function executes. This has some performance implications. However, in a small app it may not be noticeable. As the app grows large, the difference will start to materialise. One case study is here.
Arrow functions entails the same performance concerns that were there with bind
.
<input onChange={ (evt) => this.setState({ name: evt.target.value }) } value={this.state.name} />
This way of writing is definitely clearer. You can see what’s going on in the onChange
prop itself. But, this also creates new anonymous function every time input
renders. So it has the same performance penalty as above.
Edit — I came across this article thanks to Rob Wise. Turns out, there hasn’t been any performance measurements as to how slow inline functions are. Also, most of the time, they are equally performant. Check out the article for a more in-depth discussion on this.
Solutions
The best way to avoid the above performance penalty is to bind the functions in the constructor itself. This way only one extra function is created at the time of component creation, and that function is used even when render
is executed again.
It often happens that you forget to bind
your functions in the constructor, and then you get an error (Cannot find X on undefined.). Babel has a plugin that let’s you write auto-bound function using the fat-arrow syntax. The plugin is Class properties transform. Now you can write components like this —
class App extends Component {
constructor(props) {
super(props);
this.state = {
name: ''
};// Look ma! No functions to bind!}updateValue = (evt) => {
this.setState({
name: evt.target.value
});
}render() {
return (
<form>
<input onChange={this.updateValue} value={this.state.name} />
</form>
)
}
}
Read More —
- React Binding Patterns: 5 Approaches for Handling `this`
- React.js pure render performance anti-pattern
- React — to Bind or Not to Bind
- Why and how to bind methods in your React component classes?
2. Using indexes in key Prop
Key is an essential prop when you iterate over a collection of elements. Keys should be stable, predictable, and unique so that React can keep track of elements. Keys are used to help React easily reconcile(read: update) the differences between the virtual DOM and the real DOM. However, using certain set of values such as array indexes may break your application or render wrong data.
Demo
{elements.map((element, index) =>
<Display
{...element}
key={index}
/>
)
}
Problems
When children have keys, React uses the key to match children in the original tree with children in the subsequent tree. The keys are used for identification. If two elements have same keys, React considers them same. When the keys collide, that is, more than 2 elements have the same keys, React shows a warning.

Here is an example of the issues that can be caused by using indexes as keys on CodePen.
Solutions
Any key that you are going to use should be —
- Unique — The key of an element should be unique among its siblings. It is not necessary to have globally unique keys.
- Stable — The key for the same element should not change with time, or page refresh, or re-ordering of elements.
- Predictable — You can always get the same key again if you want. That is, the key should not be generated randomly.
Array indexes are unique, and predictable. However, they are not stable. In the same vein, random numbers or timestamps should not be used as keys.
Using random number is equivalent to not using keys at all since random numbers are not unique or stable. The components will be re-rendered every time even if the content inside the element has not changed.
Timestamps are unique but not stable or predictable. They are also always increasing. So on every page refresh, you are going to get new timestamps.
In general, you should rely on the ID generated by databases such as primary key in Relational databases, and Object IDs in Mongo. If a database ID is not available, you can generate a hash of the content and use that as a key. You can read about more about hashes here.
Read More —
- Index as a key is an anti-pattern
- Why you need keys for collections in React.
- On why you shouldn’t use Random values as keys.
3. setState() is async
React components essentially comprises 3 things: state
,props
and markup (or other components). Props are immutable. However, the state is mutable. Changing the state causes the component to re-render. If the state is managed internally by the component, this.setState
function is employed to update the state. There are a few important things to note about this function. Let’s look —
Demo
Problems
Focus on line 11. If you mutate the state directly, the component will not be re-rendered and the changes will not be reflected. This is because the state is compared shallowly. You should always use setState
for changing the value of the state.
Now, in setState
if you use the value of current state
to update to the next state (as done in line 15), React may or may not re-render. This is because, state
and props
are updated asynchronously. That is, the DOM is not updated as soon as setState
is invoked. Rather, React batches multiple updates into one update and then renders the DOM. You may receive outdated values while querying the state
object. The docs also mention this —
Because
this.props
andthis.state
may be updated asynchronously, you should not rely on their values for calculating the next state.
Another problem is when you have multiple setState
calls in a single function, as shown above on line 16 and 20. Initial value of the counter is 350. Assume the value of this.props.increment
is 10. You might think that after the first setState
invocation on line 16, the counter’s value will change to 350+10 = 360. And, when the next setState
is called on line 20, the counter’s value will change to 360+10 = 370. However, this does not happen. The second call still sees the value of counter
as 350. This is because setState is async. The counter’s value does not change until the next update cycle. The execution of setState is waiting in the event loop and until updateCounter
finishes execution, setState
won’t run and hence won’t update the state
.
Solution
You should use the other form of setState
as done on line 27 and 31. In this form, you can pass a function to setState
which receives currentState and currentProps as arguments. The return value of this function is merged in with the existing state to form the new state.
Read More —
- A wonderful explanation of why
setState
is async by Dan Abramov. - Using a function in `setState` instead of an object
- Beware: React setState is asynchronous!
4. Props in Initial State
The React docs mention this anti-pattern as —
Using props to generate state in getInitialState often leads to duplication of “source of truth”, i.e. where the real data is. This is because getInitialState is only invoked when the component is first created.
Demo
Problems
The constructor
or (getInitialState) is called only at the time of component creation. That is, constructor
is invoked only once. Hence, when you change the props
next time, the state won’t be updated and will retain its previous value.
Young developers often assume that the props
values will be in sync with the state, and as props
change, the state
will reflect those values. However, that is not true.
Solutions
You can use this pattern if you want a specific behaviour. That is, you want the state to be seeded by the values of props only once. The state will be managed internally by the component.
In other cases, you can use componentWillReceiveProps
lifecycle method to keep the state and props in sync, as shown here.
Beware that using componentWillReceiveProps
has it own caveats. You can read about it the Docs.
The best approach would be to use a state management library such as Redux to connect the state and the component.
Read More —
5. Components Name
In React, if you are rendering your component using JSX, the name of that component has to begin with a capital letter.
Demo
<MyComponent>
<app /> // Will not work :(
</MyComponent><MyComponent>
<App /> // Will work!
</MyComponent>
Problems
If you create a component app
and render it using JSX as <app label="Save" />
, React will throw an error.

The error says that <app>
is not recognised. Only HTML elements and SVG tags can begin with a lowercase. Hence, <div />
is okay but <app>
is not.
Solution
You need to make sure that while using custom component in JSX, it should begin with a capital letter.
But, also understand that declaring components does not adhere to this rule. Hence, you can do this —
// Here lowercase is fine.
class primaryButton extends Component {
render() {
return <div />;
}
}export default primaryButton;// In a different file, import the button. However, make sure to give a name starting with capital letter.import PrimaryButton from 'primaryButton';<PrimaryButton />
Read More —
These were some unintuitive hard-to-understand bug-makers in React. If you know about any other anti-pattern, respond to this article. 😀
I have also written Top React and Redux Packages for Faster Development
If you are still learning how to setup a React Project, this two-part series might be helpful in understanding various aspects of React build system.
✉️ Subscribe to CodeBurst’s once-weekly Email Blast, 🐦 Follow CodeBurst on Twitter, view 🗺️ The 2018 Web Developer Roadmap, and 🕸️ Learn Full Stack Web Development.