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
.
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,
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,