DEV Community 👩‍💻👨‍💻

Cover image for Complete guide to understanding the React UseState hook
Emmanuel Fordjour  Kumah
Emmanuel Fordjour Kumah

Posted on • Updated on • Originally published at efkumah.hashnode.dev

Complete guide to understanding the React UseState hook

Hello World, in this article, we take a dive on how to manage states in our React application using the useState hook.

By the time you finish reading the article, you should be able to

  • Understand state
  • How to implement the useState hook
  • How to update state in your application , and some rules in using the useState hook

Let's get started.

Understanding State in React

State is data or value that is subject to change in our application. It describes the condition of our app at any point in time.

Because we build dynamic web apps in React, we will expect the data (information) in our app to change in response to a user's action.
These actions could be a user:

  • Clicking on a button
  • Entering a value in a form field
  • Submitting a form.
  • Clicking on the next button on an image carousel.

Anytime state (data or information) changes in React, the component will re-render and display the updated information on the screen as a result of the above interactions.

State can be of any data type, it can be an object, array, a string, boolean etc.

The code below demonstrate how to use state in a component.

Using State in a component

import React from "react"
import "./styles.css";

export default function App() {
  // defining a regular variable
  const userMsg = "Start learning React Hooks";

  return (
    <div className="App">
      <h1>Hello User</h1>
      <h2>{userMsg}</h2>
      <button>Get Started </button>

    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

In the code above:

  • We defined a "regular" variable userMsg which we will call state variable (for now)
  • We then assign some value to the userMsg
  • In our JSX template, we output the information stored in the userMsg like this {userMsg}
  • Doing this, we preserve the (state) value between function calls. Anytime the app reloads, we will still see the value stored in the userMsg on the screen.

Displaying the state in our UI

With the code above, when we launch our app we should see the below on our screen:

Hello User
Start learning React Hooks
Enter fullscreen mode Exit fullscreen mode

Changing State In React.

We usually want the state (data or information) on the UI to change when an event occurs on the app. For instance, a user clicking on a button.

Let's take a look at how to change the state in our app.

  • We want the state (value or data ) to change from Start learning React Hooks to Lesson one: React useState Hook when a user clicks on the Get Started button.

  • We declared a function handleUserMsg, which will responsible for updating the value when we click on the Get Started button.

  • The handleUserMsg function will be invoked, and in the body of the function, we will assign the new value. to the userMsg variable.

See the code below

import React from "react";
import "./styles.css";

export default function App() {
  // defining a  regular variable
  const userMsg = "Start learning React Hooks";

  const handleUserMsg = () => {
// assigning a new value to the variable
   userMsg = "Lesson one: React useState Hook"
  };
  return (
    <div className="App">
      <h1>Hello User</h1>
      {/* using the state  */}
      <h2>{userMsg}</h2>
      <button onClick={handleUserMsg}>Get Started</button>
    </div>
  );1
}
Enter fullscreen mode Exit fullscreen mode

What is happening in the code above:

  • On the click of the "Get Started" button, the handleUserMsg function is invoked, and Lesson one: React useState Hook is now assigned to the userMsg variable.
  • With that, we have updated the value (this is how we learnt to assign a new value to a variable in JavaScript)

Re-rendering component when state changes.

When we assign a new value to our state variable in React, our app will need to reload (re-render) to display the updated data (information).

In the code above:

  • We assigned Lesson one: React useState Hook to the userMsg variable when the handleUserMsg function is executed.
  • When the handleUserMsg function is invoked, the state (data or value has changed), and we will expect the browser to re-render to output the code below on the user interface:
Hello User
Lesson one: React useState Hook
Enter fullscreen mode Exit fullscreen mode

Note: But it does not, let's find out why in the next section.

Exploring "reactivity" in regular variables

Even though we expect the browser to output the latest data, assigned to the userMsg variable, clicking the Get Started button does not update the state (data) from Start learning React Hooks to Lesson one: React useState Hook.

We can actually prove the new State Lesson one: React useState Hook was assigned to userMsg

  • console.log(userMsg) in the handleUserMsg function, as illustrated in the code above

The handleUserMsg() event handler is updating the regular variable userMsg. However, the change is not visible due to the below:

  • The userMsg variable declared above, is considered a "regular" variable, and "regular" variables do not carry on between renders.
  • When the component is rendered again, it does not consider any new value assigned to the regular variables.
  • Hence, any update to "regular" variables won't trigger renders (React doesn't realize it needs to render the component again with the new value).

States Should be Reactive

The value stored in the userMsg variable has changed, but the change is not reflecting on the user interface.

*This is because the value stored in the userMsg variable is only a regular variable and not reactive. *

What this means is:

  • React does not track the regular variable for changes
  • When the value changes, it will not trigger React to re-render the component to reflect the update.
  • Hence, we continue to see the initial value Start learning React Hooks on the user interface.

Updating a component with new data.

To update a component with the new data, we need to make the value 'reactive'.

The below need to happen:

  • Retain the data between renders
  • Cause React to render the component with new data (re-rendering)

To update the component with new data, we need to rely on the useState hook. It will provide these two things:

  • First is a state variable to retain the data between renders
  • Secondly, a setter function to upate the variable and trigger React to render the component again.

Using the useState hook, React can track the changes in the value, by comparing the new value to the initial value.

Let's now make our state reactive using the useState() hook.

What is the useState hook ?

The useState() hook enables you to add state variables in functional components. It allows developers to track changes, update and manipulate state inside functional components without needing to convert it to a class component.

Syntax for the useState hook

The syntax for the useState hook is as below

const [state, setState] = useState(initialState)
Enter fullscreen mode Exit fullscreen mode

How to use the useState hook

In order to use the useState() hook, we need to import it from react library like the below

import React,{useState} from "react";
Enter fullscreen mode Exit fullscreen mode
  • Now that we have imported the useState() hook, we can use it in our functional component and pass it a reactive value.
  • The reactive value is the initial value or initial state you want to display on the user interface.

In code below, we call the useState hook and pass it our initial state

... = useState("Start learning React Hooks");
Enter fullscreen mode Exit fullscreen mode

Below is the complete code, let's break it down further:

import React, { useState } from "react";
import "./styles.css";

export default function App() {

//calling the useState hook and passing the initial value
  const [userMsg, setUserMsg] = useState("Start learning React Hooks");

//function to change the state
const handleUserMsg = () => {
//update the state inside this function's body 
};
  return (
    <div className="App">
      <h1>Hello User</h1>
      <h2>{userMsg}</h2>
      <button onClick={handleUserMsg}>Get Started</button>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Under the hood of useState hook

When the useState() hook is called like this : useState("Start learning React Hooks"), it returns an array with two values:

  • First, is the initial state value
  • Next, a function called the setter responsible for updating the current state

We then use array destructuring in JavaScript to store the values returned by calling the useState() function into distinct variables: userMsg and setUserMsg.

const [userMsg, setUserMsg] = useState("Start learning React Hooks");

Enter fullscreen mode Exit fullscreen mode

In the code above:

  • The initial state is stored in the userMsg variable
  • The setter function which is responsible for updating the current state will be stored in the setUserMsg variable.
  • The variable names is entirely up to you. By convention, we name state variables like [someState, setSomeState].

Reading the state variable

The initial value stored in the userMsg variable can be read . We use it in our JSX template like {userMsg}

import React, { useState } from "react";
import "./styles.css";

export default function App() {
  // defining a user message state

  const [userMsg, setUserMsg] = useState("Start learning React Hooks");

  //function to change the state
  const handleUserMsg = () => {};

  return (
    <div className="App">
      <h1>Hello User</h1>
      {/* using the state  */}
      <h2>{userMsg}</h2>
      <button onClick={handleUserMsg}>Get Started</button>
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode
  • Now anywhere we use the {userMsg} state in our template, we will expect to output the initial value Start learning React Hooks

Updating the State using the Setter function

The setter function returned by the useState hook is responsible for updating the state in react component.

  • Anytime we want to update state in React, we should use the setter function, and not update the state directly.
  • Meaning, we can not assign a new value to a variable in React, and expect the new value to reflect on the UI. We will need to use the setter function to assign a new value.

Referencing the code above, when we click on the Get Started button, the handleUserMsg event handler function will be executed.
In the handleUserMsg function's code block, we will now update the state by doing the below:

  • We will call the setUserMsg function, and then pass it the new value we want the userMsg to store.

See the code below:

  const handleUserMsg = () => {
//updating the state variable
    setUserMsg("Lesson one: React useState Hook");
  };
Enter fullscreen mode Exit fullscreen mode
  • The setUserMsg has now updated the value stored in userMsg

Now, when we click the Get Started button, the handleUserMsg function will be executed, and in the function's body, we update the value stored in the userMsg . Since a new value has been assigned to the userMsg variable, the component will re-render and the browser will display the new value Lesson one: React useState Hook

The UI will display the below:

Hello User
Lesson one: React useState Hook
Enter fullscreen mode Exit fullscreen mode

Below is the updated code:

import React, { useState } from "react";
import "./styles.css";

export default function App() {
  // defining a user message state

  const [userMsg, setUserMsg] = useState("Start learning React Hooks");

  //function to update the state

  const handleUserMsg = () => {

//updating the state variable
    setUserMsg("Lesson one: React useState Hook");
  };
  return (
    <div className="App">
      <h1>Hello User</h1>
      {/* using the state  */}
      <h2>{userMsg}</h2>
      <button onClick={handleUserMsg}>Get Started</button>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Now using the useState hook , we can achieve the following:

  • Decide which data or information to display on the UI
  • Update that data or information using the setter function provided by the useState() hook.

Adding multiple state variables to the component

More than one state (data or information) of as many data types can be added to our component using multiple useState() calls. This helps us to display vital information on the user interface of our app.

To acheive that, use the syntax below:

function MyComponent() {
  const [state1, setState1] = useState(initial1); // first state 
  const [state2, setState2] = useState(initial2); // second state
  const [state3, setState3] = useState(initial3); // third state 
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Let's display multiple data or information (state) in our app.

import React, { useState } from "react";
import "./styles.css";

export default function App() {
// adding multiple states
  const [userName, setUserName] = useState("Emmanuel"); 
  const [posts, setPosts] = useState(7);
  const [showPosts, setShowPosts] = useState(false);

  return (
    <div className="App">
      {/* using the userName state */}
      <h1>Hello {userName}</h1>
      {/* using the  posts state  */}
      <p>You have {posts} articles published </p>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

The output of the code will be

Hello Emmanuel
You have 7 articles published
Enter fullscreen mode Exit fullscreen mode
  • In the code above, we have display information about the user , and the number of posts published.
  • It is a good practice to have multiple state variables, if the state is unrelated.For instance, the userName and posts state variables are unrelated, so we create multiple state variables.
  • If the state variables are related, it will be a good idea to put them in an object.

Using Objects as State variables

The state variable can be of any data type. So far, we have used a string and a number as state variables.
Let's take a look at using object as state variables.

import React, { useState } from "react";
import "./styles.css";

export default function App() {
//using objects as state variables.
  const [userDetails, setUserDetails] = useState({
    fullName: "Emmanuel Kumah",
    title: "Frontend Developer",
    location: "Ghana",
    bio: "Helping you understand everyline of code"
  });

  return (
    <div className="App">
      <h1>Profile </h1>
      <p>
        <strong>Name:</strong>
        {userDetails.fullName}
      </p>
      <p>
        <strong>Title:</strong>
        {userDetails.title}
      </p>

      <p>
        <strong>location</strong>
        {userDetails.location}
      </p>
      <p>
        <strong>Bio</strong>:{userDetails.bio}
      </p>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode
  • The userDetails stores anobject {...}
  • In our JSX template we can access the object values using the respective key Eg. {userDetails.fullName}

The output of the above will be:

Profile
Name:Emmanuel Kumah

Title:Frontend Developer

location: Ghana

Bio:Helping you understand everyline of code
Enter fullscreen mode Exit fullscreen mode
  • All our data was stored in an object called userDetails.
  • We accessed the data stored in the userDetails object using the key:value pair in JavaScript.
  • Our UI will then show the data needed.
  • If we want to update the data, we will use the setUserDetails function, and update the respective value.

Updating the state based on previous state

Often, we want to update the state by relying on the value of the previous state.

For instance:

  • We have a count state variable initialized to 0
  • When we click on the Count me button, we want to count the number of times the button has been clicked.
  • To achieve this, we need access to the previous count to increase it by one.

Let's see an example

import React, { useState } from "react";
import "./styles.css";

export default function App() {
     const [count, setCount] = useState(0);

  // function to increase count
  const handleCount = () => {

//updating the state based on previous value
    setCount((count) => count + 1);
  };
  return (
    <div className="App">
       {/* using the count state  */}
        <p>You have clicked the button {count} times</p>
        <button onClick={handleCount}>Count me</button>
      </div>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

What is happening in the code above:

  1. The useState() hook was called, and passed the initial value of 0.
  2. The two values returned by calling the useState(0) is stored in the count and setCount variables
  3. The inital state value 0 is stored in the count variable
  4. The setCount which is the setter function will update the count value
  5. In our JSX template, we display the count value like {count}.
  6. When the component re-renders we will see the value 0 on the screen.
  • To update the count value based on the previous value, we need to pass an updater function to the setCount
  • The updater function takes the previous state and evaluates the next state.

Below is the function to update the state

  // function to increase count
  const handleCount = () => {
    setCount((count) => count + 1);
  };
Enter fullscreen mode Exit fullscreen mode
  • Take note of the setCount((count) => count + 1)
  • The (count) => count + 1 is the updater function. It is a callback function we passed to the setter function.
  • When the setCount function is executed, it will evaluate the expression passed as arguments
  • In the expression, we access the current count , increase it value by 1 and returns the value
  • So if the current count has a value of 0, on the first click of the button, the value is updated to 1.

Whenever, you want to update the state based on value in the previous state, remember to pass the updater function to the setter function.

  • Clicking on the count me button, we call the handleCount function.
  • In the setCount puts the callback or updater function in a queue.
  • During the next render, it wil call the updater function take the previous value of count , and add 1 to it , count + 1 , and returns the **updated **value.
  • On the next click on the button, the current state value is now 1, and we add 1 to the current state, returning a next value of 2.

This is what is happening when we click on the count me button 3 times.

  // function to increase count
  const handleCount = () => {
    setCount((count) => count + 1); // setCount(0 => 1);
    setCount((count) => count + 1); // setCount(1 => 2);
    setCount((count) => count + 1); // setCount(2 => 3);

  };
Enter fullscreen mode Exit fullscreen mode
  • count => count + 1 will receive 0 as the initial state and return 1 as the next state
  • count => count + 1 will receive 1 as the previous state and return 2 as the next state
  • count => count + 1 will receive 2 as the previous state and return 3 as the next state

Where to call the useState() hook

Be aware of the following rules when using useState() hook:

  • Do not use useState() in loops, conditions, nested functions , etc
  • Use only useState() inside functional component or a custom hook
  • Call the useState() hook at the top level of the component.

Recap

In conclusion:

  • In React app, it is a bad practices to update states variables directly like we do in JavaScript
  • We should use the useState hook to track, update and manage states in function components
  • The state variable can be of any data type (strings, numbers, boolean, objects)
  • The call to the useState(initialState) hook returns two values, the initalState and a setter function
  • To update the state use the setter function
  • When the state is updated, the component will re-render to show the updated state (data) in our UI
  • You can have multiple state variables in your component.

Thanks for taking time to read my article. If you find this article, useful, kindly share it on your social media pages. Any comment or suggestion on the article will be appreciate.

Written wth love from Ghana. Me daa se (Thank you)

Oldest comments (0)

Visualizing Promises and Async/Await 🤯

async await

☝️ Check out this all-time classic DEV post