Everything You Need to Know About React useState

Everything you will ever need to know about React’s useState hook, explained with a simple React project.

Daniel Stefan
JavaScript in Plain English

--

Checked a few weeks ago what is the percentage of front-end library usage amongst my colleagues around the world. For the first time in a few years, I had the pleasant surprise to see that finally jQuery has been overthrown by a modern JS library, and the king is React 👑.

Based on these statistics, React is preferred by more than 40% of Front-End devs. That’s awesome 🕺 🎊!

As of the time of writing this article, unfortunately, I couldn’t find any statistics relevant enough to see how many React projects are developed in the old fashioned way, based on class components, how many are a mix, between classes and function components, and only function components with hooks. If I would have to guess, I would choose the mixt approach, since most projects having already a few years, would have to migrate from classes to functions if the team wants to adopt hooks.

I’ve decided to create a series of React hooks articles, that can help current React devs to easily migrate their legacy code to modern React, and also to help new coming devs and tech passionates to understand React and hooks.

In this article, we are going to get a deep dive into React’s useState .

What is useState?

useState is a React hook.

A Hook is a special function that lets you “hook into” React features. For example, useState is a Hook that lets you add React state to function components. [Source]

When do you need useState?

You need this hook, every time you are writing a functional component, and you must define some state variables into that component.

What are we building?

We are going to build a Voting Card App. I know it is a basic app, but our purpose is to understand all useState capabilities and concepts.

I prepared for you a React app wired with TypeScript and Tailwind CSS.

You can clone this repo so we can get it started 😄:

Just cd where/you/cloned/the/project and run npm i . Once the package installation is complete, run npm start and your app will be up and running.

Project structure and technologies

First, let me give you a blazing fast walkthrough of the project. It is a standard CRA project, with TypeScript preset and Tailwind CSS.

Why do I like this structure?

  • it is pretty common in most projects
  • it is more than enough for our current example
  • it has all our working files on the same depth level and in the same place
voting-card project structure

Why did I choose this setup of technologies?

  • I consider TypeScript a must in any modern web app, regardless of team size or app size, because it helps you a lot in terms of bug prevention, sharing data types, and enforcing some well-needed sets of rules
  • Tailwind CSS is a utility-first CSS framework. What does that mean? Well, it has lots of utility classes that you can use, instead of writing classic CSS. Once you get used to it, it becomes really helpful.

Let’s start coding 🚀 💻

So first of all, let’s define our data types. Let’s create a VotingUtils.ts file under our src folder. Here we will store everything in terms of data types and utility functions if needed.

src/VotingUtils.ts

Our next step is to create our voting card component. Under src folder, let’s create a file called VotingCard.tsx:

src/VotingCard.tsx

Now let’s go to our App.tsx and let’s change it with the following code:

What we’ve done so far is to set up our app and display our voting topics. But there is more than that, we already have a reusable component for all our cards, the data model that we are going to use, and a mocked data constant wired together 😄.
Now let’s add our voting icons. Let’s install react-feather:

npm i react-feather

This will provide our app with a set of open source icons, that we can use to vote our topics 👍.

Let’s editVotingCard component and add voting buttons:

If everything went fine until here, our app should look like this:

Voting card app design

Finally, we’ve reached the interesting part. We are going to use React useState hook to create 2 state variables, isLiked and isDisliked . This is how we will be able to determine if any card has been voted or not.

useState declaration

We have imported useState from react and used it above. Why do we need this? Well because we need somehow to determine, in VotingCard component, if the card is LIKED or DISLIKED.

useState hook returns a pair of values. The first value will be the state and the second one is a Dispatch function that updates the value. What does that mean? It means that isLiked and isDisliked , represent the current state value, and setIsLiked together with setIsDisliked , are the functions to update them.

Since we want to store 2 variables every time we are using useState , we can write this elegantly, with array destructuring.

Ultimately, the correct way to update our state variables is through calling their corresponding functions and passing them a new value.

Now let’s explain a bit what useState<boolean>(false) does. By appending <boolean> after useState, we are specifically telling TypeScript, that our state value is of type boolean. And the value passed in useState as a parameter, in our case false, represents the initial value that the state variables will have once our VotingCard component is rendered.

With the above in mind let’s create two functions inside our VotingCard component, one for LIKE and one for DISLIKE, attach them to our buttons, and also add an active color state on our buttons:

Now if we go into our browser and test this, we should be able to LIKE or DISLIKE voting cards.

We have added 2 new functions. Let’s break it down a bit:

The first function onLikeClicked , will set our state variable isLiked , to true , and also it will set isDisliked variable to false, regardless of its value.

The second follows almost the same behavior, but this time, we just swap the roles between isLiked and isDisliked variables.

Let’s jump into our JSX syntax and see what we’ve updated there:

We have added an onClick handler on both icons, and also, we are taking into account if any of the corresponding state variables is active, we want to highlight them both with blue or red.

An important thing to notice is that reading state in our JSX is as simple as adding our state variables into curly braces { isLiked } / { isDisliked }.

For the ones who are familiar with the class syntax, everything we’ve done so far is equivalent to the following code:

The setState callback

One thing that we can easily spot in our code so far, is that when we are trying to click multiple times on the same action (LIKE or DISLIKE buttons), the state won’t change. That’s because we are always setting that clicked button to true.

Well, before creating ifs into your onClick functions, let me tell you that, we can handle this scenario easily with our setState function callbacks.

So far, we said that setIsLiked and setIsDisliked accept a value as a parameter, but that is only half true. It also accepts a function as a parameter. That function will take the previous state value, and must return a new state value. Let’s see how this works:

setState callback function

We have changed our functions, that every time we click on onLikeClicked , to take into account the previous isLiked , value and negate it.

Same for onDislikeClicked function, and isDisliked state variable.

The setState re-rendering

One of the most important aspects of using the setState callback provided by useState , is that every time any setState callback is called, it will enqueue a re-render operation on that component. Through re-renders, React can update your state and transform UI elements on the screen.

In our case, every time we are calling setIsLiked or setIsDisliked , a re-render operation will be executed for our VotingCard component at a certain point in time. This operation queue is managed by React.

Another important thing is that, during multiple state updates, the first value returned by useState will always be the most recent state after applying updates.

The setState function identity

One thing worth mentioning is that React is guaranteeing that setState functions will have the same identity between re-renders.

What this means, is that behind the scene, the functions will point to the same reference between re-renders, and won’t be detected as a change.

Other functions or variables defined in that component, will point through different references between re-renders. We have a few exceptions with the help of React, but we will learn about them later on.

The lazy initial state

We covered the fact that useState receives a parameter that will represent the initial value for that state variable, but it can also receive a function as a parameter.

Usually, you want to pass a function as a callback and initialize the state with it, when you have heavy operations to perform.

const [likes, setLikes] = useState(() => {
return someHeavyComputationFunction()
})// or depending on how you write the function
// you can also write it something like this
const [likes, setLikes] = useState(someHeavyComputationFunction)

In this scenario is usually better to just use the lazy initial state pattern and pass a function into useState, that will return your computed state only once.

Remember: Calling the function as a param will result in the same behaviour as passing a value, and it will call that function between re-renders.

// bad
const [likes, setLikes] = useState(computeHugeAmountOfLikes())
// good
const [likes, setLikes] = useState(computeHugeAmountOfLikes)

setState updates to the rescue

Why I am saying that setState is rescuing us? It is pretty optimized, and will only update states that have different values than before. In other words, if you are trying to update the current state, with the same value, React will bail out, without rendering the children or firing effects.

Conclusion

We have learned a lot throughout this article. Let’s review everything one more time:

  • we created a Voting app
  • we added Tailwind CSS and TypeScript and used them throughout this article
  • we learned what useState is and all its secrets

This seems trivial, but trust me, you will use useState in your React journey very often, and you will have to fully understand all its quirks and secrets in order to master React and hooks.

I hope you found this article useful! In case you have any questions or suggestions, please shoot them and I will engage asap.

Stay safe and see you at the next one 🙌!

More content at PlainEnglish.io. Sign up for our free weekly newsletter. Follow us on Twitter and LinkedIn. Join our community Discord.

--

--

Developer 💻 | Writer 📘 | Passionate about JavaScript & Typescript. Currently doing my best to make the world a better place!