In React Js, we use useState in almost every component. An example of useState is given below:
const [count, setCount] = useState(0);
In above example we have created a count state where we can use setCount to update the count. But, imagine a situation where you need to compare the previous count with current count ( i.e. count ).
In this tutorial, we will create a custom hook in which we will also get a third variable i.e. prevState
const [count, setCount, prevCount] = useStateModified(0);
In above example, we have prevCount as well that we can use for our custom logic.
Step 1
Create a file in project directory with name useStateModified.jsx.
Step 2
Create a function with name useStateModified and export it as default export.
const useStateModified = () => {
};
export default useStateModified;
Step 3
Create a state using useState, take initialState as parameter in our custom hook and return state and setState.
import { useState } from "react";
const useStateModified = (initalState) => {
const [state, setState] = useState(initalState);
return [state, setState];
};
export default useStateModified;
Step 4
Till now it is nothing new and is equivalent to directly using useState in any component. To save the previous state we will use useRef hook. We will return prevState.current as third item of array.
import { useEffect, useRef, useState } from "react";
const useStateModified = (initalState) => {
const [state, setState] = useState(initalState);
const prevState = useRef(initalState);
return [state, setState, prevState.current];
};
export default useStateModified;
Step 5
If you are aware of useRef, its value won't change until and unless we update its current property ( example - prevState.current = 'some value').
So now, we will write a useEffect hook that will listen for the changes in state value. Whenever there is a change in the state, we will also update prevState.
import { useEffect, useRef, useState } from "react";
const useStateModified = (initalState) => {
const [state, setState] = useState(initalState);
const prevState = useRef(initalState);
useEffect(() => {
prevState.current = state;
}, [state]);
return [state, setState, prevState.current];
};
export default useStateModified;
When there will be change in state, it will execute the line (return [state, setState, prevState.current]) before running useEffect statement.
Lets see how we can use it.
import { useEffect, useState } from "react";
import "./styles.css";
import useStateModified from "./useStateModified";
export default function App() {
const [count, setCount, prevCount] = useStateModified(0);
return (
<div className="App">
<button onClick={() => setCount((c) => c + 1)}>Increment</button>
<h2>Count: {count}</h2>
<h2>Previous Count: {prevCount}</h2>
</div>
);
}
This way you can create custom useState hook to achieve functionality where you need previous state.
Check demo here
Top comments (6)
Thanks! It would be nice to get a TypeScript version. When I simply add
T
as the generic parameter the return type becomesT | Dispatch<SetStateAction<T>>
which doesn't work seamlessly. Here is what I have done;My real use case example;
OK thanks, I get it now, the reference is actually always the same value as the state, its just because setting the ref doesn't cause a re-render so its showing the old value always, not actually holding the previous value.
So if any part of the code depends on that ref to hold the previous value, it would produce invalid results...
Would be nice to have a working demo somewhere of this? a bit confused on how this works
Added a demo link in bottom of the content
Sure will share online link