12 - States

This has to be the most important chapter of this book. This chapter answers two very important questions:

  • Why should we care about virtual DOM?

  • Why is React called React?

We'll find answers to both the questions in this extremely important chapter. If you recall from the section 1, React uses a concept called virtual DOM. Just to reiterate:

The virtual DOM (VDOM) is a programming concept where an ideal, or “virtual”, representation of a UI is kept in memory and synced with the “real” DOM by a library such as ReactDOM. This process is called reconciliation.

With the help from the virtual DOM, the React world doesn't need any code like this:

//GET from DOM
document.getElementById("some-id").value; 

//SET INTO DOM
document.getElementById("some-other-id").innerHTML = "some HTML"; 
document.getElementById("some-other-id").value = "some value";

There is no need to work with the real DOM directly in React. Again, you can go to real DOM, but that'll defeat the purpose of using React in the first place. All the above operations (GET and SET) happen in React through a concept called states. This is the third foundation of the React world.

React state

A state in React is a snapshot of the current state of a component. A component can have any number of states. The state can be simple literals or JS objects. At any point in time, the state represents the most up-to-date view of the component. The dynamic parts of the components are good candidates for states. The static parts of the components doesn't need a state. In other words, the primary use case of state is to control dynamicity.

Here is an example of a very simple component called App. There is a title & a button inside it. We want the button text to change from 'Click me!' to 'Clicked' as soon as the button is clicked. The h1 element is static, so not a good use case for state. The changing nature of button text is a great candidate for state.

App.js

function App() {

  function someHandler() {
    /* Update button text here */
  }

  return (
    <div>
      <h1>Some title</h1>
      <button onClick={someHandler}>Click me!</button>  
    </div>
  );
}

State lifecycle

A React state goes through three different phases in its lifecycle:

  • Created: The state is created.

  • Used: The state gets used in the component

  • Updated: The state gets updated in the component

If a state that has been used in a component gets updated, it'll trigger a re-rendering of the component. This is why React is called React. We'll learn more about this shortly. First, let's see how to create a state.

To demonstrate all the features, we'll be using a simple app that has a button with the text "I got clicked <N> times", where <N> is the number of times the button has been clicked. This simple button will take us through the state lifecycle. The initial code of this component is:

App.js

import "./App.css";

function App() {
  return (
    <div className="App">
      <button className="btn">I got clicked 0 times!</button>
    </div>
  );
}

export default App;

Create state

The first step in using state is to create one using a React hook/API called useState. Hooks are a way for function components to use advanced React APIs. We don't have to worry about hooks much, but we'll be using them a lot on the way.

In the simplest form, the useState API takes one input and produces two outputs:

  • Input: The initial value of the state. It could be empty at the time of creation. The value of the state could be simple type or object type. Any JS type is okay to use in the state.

  • Output: The useState API returns two very important items - a variable that contains the current value of the state and a function that must be used to update the value of the state. We can name them anything we like. Of course, the names will be relevant to the usage of the state data.

The following code creates a state and stores the output into appropriate variables.

const [ var1, setVar1 ] = React.useState();

The names of var1 and setVar1 are entirely up to the user. But they should be relevant to the data present in the state. The var1 variable always contains the current up-to-date value of the state. The function setVar1 has to be used to update the value of the state. Note that state value has to be updated through the returned update function. If you change the value directly inside var1, you won't get the desired results. This is the reason why state variable should be kept constant.

The following code creates a state with some initial value:

const [ btnText, setBtnText ] = React.useState("Click me!");

This must be looking familiar to you. We're creating a state with initial value of "Click me!". The most up-to-date value of state is saved in btnText. Anytime you need to update the state, you can use setBtnText with the new state value.

console.log(btnText); //Prints the current up-to-date value of the state

setBtnText("Click me again!"); //Updates the value of the state

The final piece of code creates a state with JS object:

const [ userData, setUserData ] = React.useState({
  name: "Mayank C",
  city: :San Jose",
  phone: "+1408 999 1111"
}); 

Let's add state creation to the button clicking problem. We'll create a state called 'times' and an update function called 'setTimes'. The initial value of the state will be 0. This is because the button has not been clicked at initial rendering.

App.js

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

function App() {

  const [times, setTimes] = React.useState(0);

  return (
    <div className="App">
      <button className="btn">I got clicked 0 times!</button>
    </div>
  );
}

export default App;

A state that's created, but not used, is of no use. You can even call it 'unused' data. Let's see how state gets used.

Use state

The first part of the state, i.e. the state variable, can be used anywhere just like a regular JS variable. In most cases, the state variable goes into the component's JSX. This way, the component gets dependent on the state.

The updated code of the button clicking problem is as follows. The variable "times" is used in the JSX. This makes the component dependent on the state changes. As soon as state changes, the component gets re-rendered. In other words, UI reacts to state changes.

App.js

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

function App() {

  const [times, setTimes] = React.useState(0);

  return (
    <div className="App">
      <button className="btn">I got clicked {times} times!</button>
    </div>
  );
}

export default App;

This is all about the usage of the state. This is the simplest of all three.

The last one is where updates to the state triggers a re-rendering of the component. This is the reason why React is called React.

Update state

The primary reason to use state is that any change of state triggers a component re-rendering. The only condition is that, the state must change through the provided update function.

The update function takes two kinds of inputs:

  • The new value of the state: You can directly supply the new value of the state. This is useful when the new value is independent of the previous value.

  • The callback function: You can also define a callback function that will give you the previous value of the state and expects you to return the new value of the state. This is useful when the new state value is dependent on the previous value. Let's say you're using a JS object in state. You may want to change only a single attribute. In such case, you can get the current object in the callback function, make required changes to it, and then return it from the callback function.

In the button clicking problem, the new value is one more than the previous value. We can use the callback function to update it. We also need to write a button click handler function that'll make the call to the update state function. The click handler function will be called updateTimes.

The update code for the button clicking app is:

App.js

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

function App() {
  const [times, setTimes] = React.useState(0);

  function updateTimes() {
    setTimes((prev) => ++prev);
  }

  return (
    <div className="App">
      <button className="btn" onClick={updateTimes}>
        I got clicked {times} times!
      </button>
    </div>
  );
}

export default App;

Let's do a quick run (see the GIF below):

Mark this is a landmark in your React journey. You've just gone through the ultimate power of the React framework. Compared to the traditional world, we don't worry about:

  • Getting the current value of the button text from DOM

  • Updating the button text into the DOM

We don't use any API like document.getElementById or document.querySelector. As you're coming from the vanilla JS or jQuery world, here is a quick mapping:

Vanilla JS / jQueryReact

document.getElementById("button").value; $("#button").val()

times

document.getElementById("button").value = 1; $("#button").val(1)

setTimes(1)

Color changing button

To reinforce our learning on this very important topic, let's do another exercise.

Problem

Create a button that changes its color to a random color on each click. The random color can be picked from a list of basic colors, or a random hex can be used.

Solution

We will generate a random hex for the next color. We'll use a state for the button color. Whenever the button is clicked, we'll update the state from a randomly chosen color. As the new color has nothing to do with the previous color, we don't need to use the previous state. We can directly set the new state (or the new color).

App.js

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

function App() {
  const [btnColor, setBtnColor] = React.useState("white");

  function updateColor() {
    setBtnColor("#" + Math.floor(Math.random() * 16777215).toString(16));
  }

  return (
    <div className="App">
      <button
        className="btn"
        onClick={updateColor}
        style={{ backgroundColor: btnColor }}
      >
        Click to change my color
      </button>
    </div>
  );
}

export default App;

Random UUID

This is the last example before we close this section.

Problem

Create a button that'll show a random UUID on each click. The UUID can be show directly on the button.

Solution

This is a pretty simple problem. We'll use a state for the UUID. This state will be directly used as the button text. We can use crypto.randomUUID() API for generating a random UUID. As UUIDs are totally random, we don't need the previous state value.

App.js:

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

function App() {
  const [uuid, setUuid] = React.useState(crypto.randomUUID());

  function updateUuid() {
    setUuid(crypto.randomUUID());
  }

  return (
    <div className="App">
      <button className="btn" onClick={updateUuid}>{uuid}</button>
    </div>
  );
}

export default App;

--

That was all about states. In the next section, we'll cover the remaining important topics like making API calls & simulating clicks. That should finish enough basics to start migrating the app.

Last updated