RemixNode's Blog

TwitterGitHub
ReactJS useState Hook - lazy initialization and previous state

ReactJS useState Hook - lazy initialization and previous state

ReactJS useState hook is a great way to manage the state of the functional components. Let's learn a couple of interesting usages about it.

The State of a Component

ReactJS is a component-based user interface library. Each of the components we create should have an individual responsibility. In the real world ReactJS application, components need to interact by exchanging information or data. There are several ways components can talk to each other, like passing props, creating a single source data context, or a store to read and write data.

While this interaction makes the app dynamic, components also need to handle private information. Every component needs to track this information change to drive an outcome. The component's private information(or data) is called state.

component-composition.png

The image above shows the representation of the component's state and the props it passes across.

The useState Hook

With the invent of the functional components in ReactJS, we can perform state management using a famous hook called useState. We declare and track the component's state using this hook. It takes an optional argument as the initial state value and returns the current state and a function to update it.

const [counter, setCounter] = useState(1);

In the above code snippet, we initialize the counter state with the value of 1, and the variable counter will always give us the current state value. If we want to update the state, we do not directly update the counter variable. Instead, we will explicitly call the setCounter() method and update the state value.

const incrBy3() => {
   setCounter(counter + 3);
}

The method incrBy3() update the current state by incrementing the counter value by 3. The update of a state triggers the re-rendering of your component. It means the useState hook gets called internally to provide you with the updated counter value that you may use in your JSX.

<div className="counter-box">
    <span>{ counter }</span>
    <button onClick={incrBy3}>+ 3</button>
</div>

The above snippet shows the JSX code that renders the current state value(counter) and a button click-event that uses the updater function(setCounter()) to change the state.

If we put everything together in a ReactJS component, it will look like this,

import React, { useState } from 'react';

export default function App() {
  const [counter, setCounter] = useState(1);

  const incrBy3 = () => {
    setCounter(counter + 3);
  };

  return (
    <div className="container">
      <h1>Increment By 3</h1>
      <div className="counter-box">
        <span>{ counter }</span>
        <button onClick={incrBy3}>+ 3</button>
      </div>
    </div>
  );
}

The output,

output-1.gif

That's great!!! But, what about the Lazy Initialization and Previous State 😲? Oh yes, let's get to that.

Interesting Facts of useState Hook

A few points to emphasize here that we often ignore.

  • With the useState hook, the state gets created only at the first render using the initial value we pass as an argument to it.
  • For every re-render (subsequent renders after the initial render), ReactJS ignores the initial value we pass as the argument. In this case, it returns the current value of the state.
  • ReactJS provides us with a mechanism to get the previous state value when dealing with the current state value.

That's all about the interesting facts, but they may not make much sense without understanding their advantages. So, there are two primary advantages,

  • We can perform a lazy initialization of the state.
  • We can use the previous state value alongside the current one to solve a use case.

Let's learn them with examples below.

How to perform Lazy Initialization of the State?

If the initial state value is simple data like a number, string, etc., we are good with how we have created and initialized the state in the above example. At times, you may want to initialize the state with a computed value. The computation can be an intense and time-taking activity.

With the useState hook, you can pass a function as an argument to initialize the state lazily. As discussed, the initial value is needed only once at the first render. There is no point in performing this heavy computation on the subsequent renders.

const [counter, setCounter] = useState(() => Math.floor(Math.random() * 16));

The code snippet above lazily initializes the counter state with a random number. Please note, you don't have to do this always, but the knowledge is worthy. Now you know you have a way to perform lazy state initialization.

How to Get the Previous State Data and Use it?

The useState hook returns a function to update the state. In our example, we know it as the setCounter(value) method. A specialty of this method is, you can get the previous(or old) state value to update the state. Please take a look into the code snippet below,

const incrBy3 = () => {
    setCounter((prev) => prev + 3);
};

Here we pass a callback function to the setCounter() method gives us the previous value to use. Isn't that amazing?

Please find the updated source code with lazy initialization and previous state value usages from here: The Source Code on Stackblitz

Conclusion

To conclude, ReactJS's standard hooks got lots to offer when you use functional components. The useState hook helps us create and track the state changes. Knowing the extra bit of information about the lazy initialization and previous state value may help us deal with situations.

Are you new to RaectJS and the ecosystem around it or want to understand the fundamentals? I have started a series of videos to help you understand ReactJS practically and fundamentally. Here is a video from the series you may find helpful.


That's all for now. I hope you found this article insightful.

I share my knowledge on,

  • 🌐 Web Development(JavaScript, ReactJS, Next.js, Node.js, so on...)
  • 🛡️ Web Security
  • 💼 Career Development
  • 🌱 Opensource
  • ✍️ Content Creation

Let's connect,