Join us live as we unveil the all new Hygraph Studio!

useState() Hook in React - A Complete Guide

In this guide, you will learn everything you need to know about the useState() hook, how it works, and some rules that guide how it works alongside examples for better understanding.
Joel Olawanle

Joel Olawanle

Sep 22, 2022
useState React

React is a free, open-source JavaScript frontend library that you can use to build user interfaces by breaking up your project into components. It was released in 2013 with two significant ways of defining components: classes and functions.

Before React v16.8 in 2019, which included React hooks, developers have always had to use class components for data management (with states) and some additional operations (like lifecycle methods), relegating functional components to only be used for rendering UI.

Since the introduction of React Hooks in React v16.8, you can now manage data via states in functional components and work with lifecycle methods. This has led to many people adopting functional components over class components due to their cleaner syntax.

#What are Hooks?

Hooks are special functions that let you "hook into" various React features within functional components. These hooks make certain features like working with data fun. For example, in class components, when you need to store and update a value, our code will look like this:

class App extends component {
constructor(props) {
super(props);
this.state = { count: 0 };s
}
incrementCount = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>{this.state.count}</p>
<button type="button" onClick={this.incrementCount}>
Increment Count
</button>
</div>
);
}
}

But with the useState() hook, you can handle this with a lot cleaner and direct syntax:

import React, { useState } from 'react';
const App = () => {
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount(count + 1);
};
return (
<div>
<p>{count}</p>
<button type="button" onClick={incrementCount}>
Increment Count
</button>
</div>
);
};

This might look similar, but when you start working with more complex data, you begin to see the importance of a clean syntax.

Don't get confused, you will learn how everything works in this guide, but the major point is that you no longer use .this keyword. Hooks do not have to do with data alone but also like cycle methods with useEffect() hook and many other features.

#What is useState() hook?

In React, the state is data or properties you can use in our application. States are mutable, meaning their value can change, and for that, the useState() hook is used to handle and manage your states.

The useState() hook allows you to create, track and update a state in functional components. This hook is created by React and doesn't need to be installed when you create your React project.

Declaring a state to React with useState()

To use the useState() hook, you must first import it from React, or you will have to append it like React.useState() anytime you have to create a state. The useState() hook takes in the initial value of the state variable as an argument. This value could be of any data type, such as string, number, object, array, and lots more.

import React, { useState } from 'react';
const App = () => {
const number = useState(0);
const string = useState('')
const object = React.useState({})
const array = React.useState([])
return (
// ...
);
};

And this value can be used directly within your project using curly braces alongside the state variable:

const App = () => {
const [count, setCount] = useState({ number: 5 });
return (
<div>
<p>{count.number}</p>
</div>
);
};

It is essential to know that the useState() hook does not return just a variable, as seen above, but it also returns a second value, a function that you can use to update the state value.

use state explained

Update a state with useState()

The second value, destructured when creating your state, is a function you can use to update or set your state. This can be called anything, but it is best practice to use the variable name with a prefix of set.

const [state, setState] = useState(initialValue)
const [count, setCount] = useState(initialCount)
const [anything, setAnything] = useState(initialAnything)

An excellent way to see how this works is if you have a number whose default value is set to 0, and then you want this number to increase when a button is clicked. This means that when the button is clicked, you want your state to update by adding one to whatsoever number:

const App = () => {
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount(count + 1);
};
return (
<div>
<p>{count}</p>
<button type="button" onClick={incrementCount}>
Increment Count
</button>
</div>
);
};

In the code above, count stands for the state variable, setCount is the function used to update the state, while 0 is the initial value.

The state value is passed on the markup to the web page alongside a button with an onClick() event. In the click event, an increment function is passed, which we called and used the setCount function to increment the count state.

const incrementCount = () => {
setCount(count + 1);
};

You can also decide to add this function inline within the onClick() event on our button tag:

<button type="button" onClick={() => setCount(count + 1)}>
Increment Count
</button>

#Using multiple state variables

Creating individual states for data that you can combine into one state won't be advisable when working with real-life data. For example, if you want to create a state for user data such as name, age, and hobby.

const App = () => {
const [name, setName] = useState("John Doe");
const [age, setAge] = useState(20);
const [hobby, setHobby] = useState("reading");
return (
// ...
);
};

You can combine all these into an object and then access them within our JSX via the dot operator:

const App = () => {
const [userDetails, setUserDetails] = useState({
name: 'John Doe',
age: 20,
hobby: 'Reading',
});
return (
<div>
<h1>{userDetails.name}</h1>
<p>
{userDetails.age} || {userDetails.hobby}
</p>
</div>
);
};

This will output:

current state

Another way to do this, if you don't want to use the dot operator, would be to destructure. When you have nested objects, this method would no longer be ideal, but since you have few items, you can destructure.

const App = () => {
const [{name, age, hobby}, setUserDetails] = useState({
name: 'John Doe',
age: 20,
hobby: 'Reading',
});
return (
<div>
<h1>{name}</h1>
<p>
{age} || {hobby}
</p>
</div>
);
};

#Using an object as a state variable

When working with real-life data, there is a high tendency that these data will be received as an object that you might struggle with when calling or updating.

For example, if you have a nested object with the user's details, you can call data with the dot operator noting how they are nested.

const App = () => {
const [userDetails, setUserDetails] = useState({
myName: {
firstName: 'John',
lastName: 'Doe',
},
age: 20,
hobby: 'Reading',
});
return (
<div>
<h1>
Hello {userDetails.myName.firstName} {userDetails.myName.lastName},
</h1>
<p>
{userDetails.age} || {userDetails.hobby}
</p>
</div>
);
};

You can update a particular data and then use the spread operator to ensure that other data are copied back into the object. For example, if you want to update the first when a button is clicked, it changes from John to Jane:

const App = () => {
const [userDetails, setUserDetails] = useState({
userName: {
firstName: 'John',
lastName: 'Doe',
},
age: 20,
hobby: 'Reading',
});
const changeName = () => {
setUserDetails({
...userDetails,
userName: {
...userDetails.userName,
firstName: 'Jane',
},
});
};
return (
<div>
<h1>
Hello {userDetails.userName.firstName} {userDetails.userName.lastName},
</h1>
<p>
{userDetails.age} || {userDetails.hobby}
</p>
<button onClick={changeName}>Change Name</button>
</div>
);
};

In the code above, a changeName function is created that will be triggered when the button is clicked. In this function, we changed the firstName.

It is important to note that if you decide only to change the firstName, the other information will disappear. This means we have to add the previous data to our object with the spread operator.

const changeName = () => {
setUserDetails({
// copy all other object values
...userDetails,
// recreate the nested object that contains the field to update
userName: {
// copy all the values of the object
...userDetails.userName,
// overwrite the value to update
firstName: 'Jane',
},
});
};

This can be very tricky when dealing with multiple nested objects. Still, when you bear this in mind, you first copy all the object keys/values using the spread operator. You then create the nested object you want to update, then call all its values, and so on, till you get to the point where the value you want to update is located; then, you will update the value.

#Initializing State as a Function

In a situation where you need to make a computation of which would best be done within a function. You can initialize a state with a function that returns a value.

const expensiveComputation = () => {
let number = 50;
let newNumber = (50 % 10) * 10 - number;
return newNumber;
};
const App = () => {
const [calc, setCalc] = useState(() => expensiveComputation());
return (
<div>
<p>{calc}</p>
</div>
);
};

Notice that you created the function outside the component.

#Important things to know when using useState() hook

There are two major rules to remember when using hooks, which include useState() hook.

  • ONLY call useState() Hooks at the top level of your component: This means that within your component, you can only call hooks at the top and not inside any function, loop, nested function, and conditions. This helps React preserve and call hooks in the same order each time a component renders.
// Never do this ❌❌❌❌
if(condition){
const [count, setCount] = useState()(0);
}
for (let index = 0; index < 25; index++) {
let [count, setCount] = useState()(0);
}
const nestedFn = () => () => {
const [count, setCount] = useState()(0);
}
  • ONLY call useState() hooks inside a functional component: Hooks are created for functional components and should only be used in functional components. They don't work in class components. If you need to use state in the class component, then use the state object.

#Alternatives for useState() : useReducer

React has so many other hooks, and all these hooks perform specific functions. Some and more complex and perform more complex operations than others. A very good alternative to the useState() hook is the useReducer() hook.

When you have multiple hooks, which are interwoven, it is best to use the useReducer() hook. Its syntax is very similar to useState(), but you should only use the useState() hook when individual States do not affect each other.

// Syntax
const [state, dispatch] = React.useReducer(reducerFn, initialState, initFn);

With useState(), you use the state update function, which is the second value to update your state; with useReducer, you use the dispatch function and pass it an action (an object with at least a type property).

dispatch({type: 'increase'})

You then create a reducer function to handle actions and states, similar to how it's done in Redux. You can learn more on when to make use of useState() or useReducer() in this video comparison.

#Conclusion

In this guide, you have learned what hooks mean, how to use the useState() hook, and the rules guiding using hooks in React. To iterate, only use the useState() hook within a functional component and never use it within a nested function, loop or condition but at the top level of your functional component.

Blog Author

Joel Olawanle

Joel Olawanle

Joel Olawanle is a Frontend Engineer and Technical writer based in Nigeria who is interested in making the web accessible to everyone by always looking for ways to give back to the tech community. He has a love for community building and open source.

Share with others

Sign up for our newsletter!

Be the first to know about releases and industry news and insights.