How to get the PREVIOUS state in React?

How to get the PREVIOUS state in React?

ยท

3 min read

Let's start by asking ourselves why would we need the previous state? There might be some cases where you need to render a different view which also changes the current state based on the previous state.

Consider a simple example - you have an ON/OFF toggle button. Let's get a bit adventurous and not use a boolean state. Instead, we use the string representation ON or OFF. This example is only to simplify the process of how to get the previous state.

We start defining a simple component <Toggle /> which renders the button with the initial state as ON.


import React, { useState } from 'react';
import ReactDOM from 'react-dom';

const Toggle = () => {

  const [buttonState, setButtonState] = useState("ON");

  const handleClick = () => {
     // our toggle logic will go here
  }


  return (
    <div>
      <button onClick={handleClick}>{buttonState}</button>
    </div>
  )
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

Here comes the key part. To get the previous state, we use the useRef() hook in React.

What's the use of useRef() here?

useRef() is a really powerful hook in React which provides a ref which stores a value that is similar to a state. Refs can have other use cases as well. This is just one of them. The main difference being, React does not re-render the UI if the ref's value changes unlike when a state's value changes.

We use a custom usePrevious() hook that would give us the previous value. The ref contains a .current property that holds the value. We will look into how it works down below.

import React, { useState } from 'react';
import ReactDOM from 'react-dom';

// hook that would return the ref to the previous value
function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref;
}


const Toggle = () => {
 // component logic..
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

Finally, adding our toggle logic inside the handleClick function. We'll also render the previous state on the UI to see it clearly.

import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref;
}


const Toggle = () => {

  const [buttonState, setButtonState] = useState("ON");

// getting the value of the previous state
  const previousState = usePrevious(buttonState);

  const handleClick = () => {
// toggling logic
    if(previousState.current === "ON") 
      setButtonState("OFF");
    else 
      setButtonState("ON");

  }
  return (
    <div>
      <button onClick={handleClick}>{buttonState}</button>
<!-- we also render the previous state to see what changes happen -->
      <p>Previous State: {previousState.current}</p> 
    </div>
  )
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

How does this work?

During the first render when the usePrevious() custom hook is called, the useEffect does not run the () => ref.current = value until the first render finishes. So the hook returns undefined as the ref's value initially. Only during the next re-render, theuseEffect runs with the value that was passed to it in the previous re-render(i.e. "ON" from the first render) which enables it to store the previous value.

So this is what happens rendering in order,

  1. state = "ON", ref = undefined
  2. state = "OFF", ref = "ON" (value of ref comes from the previous render)
  3. state = "ON", ref = "OFF" and so on..

Hope you understood how you can use refs to get the previous state value. Share your comments and feedback down below! ๐Ÿ‘‡

image.png image.png

Ciao! ๐Ÿ‘‹

ย