Back to all blogposts

React hooks best practices in 2021

Jakub Szeja

Jakub Szeja

Frontend Developer

Hooks have become a part of React relatively recently, but they already have a major impact on how developers solve various problems with state management and more. But to take full advantage of React hooks requires experience. But while there may be some truth to the saying “no pain, no gain”, it doesn’t mean that you have to be the one to suffer. Through my own experience with hooks, I was able to come up with a lot of React hooks best practices so that you can get it right from the start.

Hooks in React made their debut at the beginning of 2019 and quickly gained popularity among React developers. Thanks to hooks, one could create stateful components without JavaScript classes and extending the React.Component. Hooks made our code simpler, more concise and readable. 

Getting rid of the classes made a lot of the boilerplate code disappear. For example, there is no longer a need to use the constructor and call super with props every time. There is also no need to bind methods or think about what this is at the moment. 

When using hooks, it is worth remembering a few rules, so that the code works as expected and is understandable to others. In this article, I will tackle two of them – useState and useEffect.

React hooks best practices – the basics

I hope you already know this one, but I must mention it: Hooks cannot be called conditionally. You cannot put them in loops or nested functions inside your component. 

So what to do if you want to call a hook conditionally? Just put the condition inside your hook. 

Why is this so important? Hooks can be called multiple times in a given component and React operations are based on the order in which they are called. Not following this would introduce bugs to your application and fatal errors.

Fortunately, there is an eslint plugin created by React team which enforces you to follow this rule. It is called eslint-plugin-react-hooks and you can find it on npm. If you use Create React App for your application, it is installed by default. 

This plugin will work not only with React hooks but also with every custom hook. But for this to happen, you have to follow a specific convention – the hook name must start with use.

You can install the eslint-plugin-react-hooks plugin using the popular npm package manager

useState React hook used safely

As I mentioned before, when using React hooks (and generally in programming), it is worth sticking to well-known conventions. When naming values from useState, it is assumed that the elements are called:

By doing so, you will be able to easily find functions linked to the state they update and your code will be consistent across the application.

The useState hook is great for storing the state for simple values such as booleans or form inputs. Unlike the state from classes, you are not limited to a single object. With useState , you can (and you should) create separate values for each case.

For example, when working with form fields, instead of keeping user info in one state like this:

It’s better to create separate fields for each case:

Of course, there is no strict limit here and useState can indeed store objects, but there is a thing you need to remember. In class components, when you update state using this.setState, it is merged with the old one. With useState, the update function replaces the old state with a new one; there is no merging at the end. Take a look at this example:

If you updated the state like this, you would lose all your previous data. Don’t worry, there is a way to handle it. Similar to the this.setState class, instead of just passing new values, you can pass a function that receives the previous state. So you could do something like this:

Or you could do it with the spread operator:

One more important thing to note here. When you are updating values based on the previous state, you shouldn’t do it that way: 

Do you know where the problem is? State updates work asynchronously, so you should never rely on the previous values in this manner. Instead, you should always use a function, even when not dealing with objects.

useEffect is the one React hook to rule them all

The useEffect hook allowed for logical separation of functionalities in the component, depending on what they do, instead of when they should occur. In the class, we had to keep unrelated functionalities in given lifecycle methods. With useEffect, we can split our code into smaller chunks. And just like with useState, you should not worry about calling useEffect several times for each case.

When working with useEffect, in most cases you want to include a dependencies list. Why? Let’s say you want to create a timer in your application. You call the setInterval function inside useEffect to update the timer variable every 1000 ms.

By default, useEffect runs on every render. So when you update your component state inside useEffect, state update triggers re-render. Then, useEffect runs again, which updates the state and it goes infinitely. Or at least until your browser or API crashes. Luckily for us, the React team thought about it and added an eslint rule, which reminds one of this. It is called exhaustive-deps and it is included in eslint-plugin-react-hooks.

But in this case, dependency with a timer would not help, because you would still call setInterval over and over again after the timer variable is updated. Of course, you could use setTimeout instead of setInterval, but there are 2 other things that can be done with useEffect to pass an empty array as a dependency or use a cleanup function.

If you pass an empty array as a dependency, it will tell React that this effect doesn’t depend on any value, so it will never run again.

Another thing you can do is to use a cleanup function, which is similar to componentWillUnmount from the class component. Everything which is returned from function passed to useEffect will be called when the component is removed. Moreover, if a component is rendered multiple times like in that case, the last effect is clean before the next is executed. Here you could simply return clearInterval with the id of setInterval.

The ESlint plugin for React makes it a lot easier to take full advantage of hooks

React hooks best practices – summary

That’s it for this little React hooks tutorial. There is a lot more to cover about this topic, but I hope you found some useful tips for these two React hooks examples – useState and useEffect.

As you can see, hooks allow us to simplify components in our application by a lot. However, when using them, there are some gotcha parts that need to be considered. If you follow these guidelines, your app will be less prone to bugs and also more understandable for other developers.

💡 Here are stories you might react well to

State of Frontend 2024

👨‍💻 Help the Frontend community! Answer the State of Frontend 2024 global survey. Takes less than 10 mins.

I want to help

The Software House is promoting EU projects and driving innovation with the support of EU funds

What would you like to do?

    Your personal data will be processed in order to handle your question, and their administrator will be The Software House sp. z o.o. with its registered office in Gliwice. Other information regarding the processing of personal data, including information on your rights, can be found in our Privacy Policy.

    This site is protected by reCAPTCHA and the Google
    Privacy Policy and Terms of Service apply.

    We regard the TSH team as co-founders in our business. The entire team from The Software House has invested an incredible amount of time to truly understand our business, our users and their needs.

    Eyass Shakrah

    Co-Founder of Pet Media Group


    Thank you for your inquiry!

    We'll be back to you shortly to discuss your needs in more detail.