useState() React Hook : An intro and common pitfalls

ยท

5 min read

Introduction

React.js is a open source library which was made my developers at Facebook(now Meta) to create user interfaces using components. According to StackOverflow, React.js is the most popular library being used to build frontend UI interfaces using components approach.

If you have ever tried to learn React recently, you must have come across hooks. React Hooks were introduced in React 16.8 in 2019 which let developers use state and other React features without writing a class component by using functional component instead. Hooks are nothing but special functions provided by React which has superpowers of their own. One of the most common hook used is useState hook. This hook is the most fundamental hook and in this post we are going to learn about the same.

Here is what is this useState hook, why and how it is used, and when you should and shouldn't use it.

Note : In this post, I will only be using functional components to explain the concepts.

What is useState() hook?

Before we learn about useState() hook, we need to understand what a state is.

A state is nothing but a data that can change. So, actually a state can be any type of data or value. For example- It can be an array, or an array of objects, boolean value, null etc.

A useState() hook is used to create/update state variables. This hook provides us two things the first one being variable which gives you state value, and second thing being a function which is used to set/update the state. Let's see how it works.

How to use useState() hook?

Generally, this is how useState() hook is used.

const [state, setState] = useState(some_intial_value);

The state is our state variable which gives us the current value of our state which is equal to some_intial_value intially, which can be anything. The second thing is setState which is a function to update our state variable. The setState function will be used mostly in an event handler like onClick, onSubmit etc.

You can use create-react-app or npm create vite@latest to setup demo project locally or use the CodeSandbox below.

We start off by importing useState hook from react in our App.jsx . Then, we write array destructing for useState hook using the line below.

const [count, setCount] = useState(0); // ๐Ÿ‘ˆ 2. Provide initial value

useState hook need to be provided with a initial value for the state count which is 0 in this example. The usage of square brackets above shows that array destructing has been used here. This means useState() is returning an array. The first thing being returned is a state variable which we name as count and the other one is a function to update state which gets named as setCount .

Tip : It is good to give self descriptive names to state variable and also to the setter function to better understand its use and also to provide an appropriate initial value.

Using a state variable

The variable count can be used anywhere the code after it has been declared. To use it in JSX, you need to wrap it inside curly braces like this {count} .

Updating the state using setState()

To update the state variable to a new value, we add a trigger to update our state. For example, I have added a button and attached a trigger(onClick in this case) to it.

  const handleClick = () => {
    //A handler function to update state using setCount()
    setCount(prev => prev + 1); // Updating state using a updater function
  };
//..
//..
//..
//..
<button onClick={handleClick}>Increase count</button>

This button when clicked fires a callback function handleClick which updates the state using setCount. Here, prev => prev + 1 is called a updater function. This ensures that the pending state is used to calculate next state. What does this mean, can't we just pass new value to set function like setCount(count+1)? Yes we can. But, we will discuss this in detail where we will learn more about common pitfalls of useState hook. But for now, remember that if you need to update the state it is always a safe choice to use this method.

When the state is updated successfully, it causes a re-render of the component, resulting in reflecting the updated value of count on screen.

Caution

Never try to update a state variable directly. Always replace them by using a modified copy. For example count = count + 1; . This will end up giving you an error of TypeError: Assignment to constant variable . Even if you change the const keyword to let or var . Updating the state directly using count = count + 1; won't trigger a re-render and hence the new value won't reflect on screen, because only setCount(count => count+1) can update the count and trigger a re-render here.

Similarly, the example below won't work.

const [person, setPerson] = useState({ name : "Sujal Gupta",
                                       age : 23,
                                      });
person.name = "Nitin Gupta";
// ๐Ÿ‘‡ Doesn't do anything
setPerson(person); 
/*********************************/
// ๐Ÿ‘‡ Works fine, because here we replace the object
setPerson({...person,        
           name : "Nitin Gupta"
           });

The code above doesn't do anything because the set function only updates the state when there is a difference between previous state(person) and new value(person) being used to update the state. Here, person is already updated so, setPerson doesn't do anything and won't cause a re-render, because React ignores this update.

Why the const state variables doesn't throw error?

In the CodeSandbox above, I used const keyword to declare state variables. I wasn't able to mutate them directly as it would throw a TypeError, but I was able to do the same using setCount function. Why is that, shouldn't it give same error as we are modifying something constant.

In reality the count that we were using is actually a copy of state variable's value and is not the actual state variable. The actual state variable is maintained by React internally. So, when we update the state using setCount, we are not doing a re-assignment but instead React updates it's internal state. The count which we were referring to uptil now, is not the actual state variable but it is a "Snapshot" of the internal state for that particular render. So, we are not modifying any const variable at all.

References

ย