Robin van der Vleuten

Use previous value through a React hook

React Hooks took me a little while to get used to after they were introduced in React v16.8.0. After using them for a while, the value became clear: custom hooks let you move repeated component logic into small functions.

After building several applications with hooks, I ended up with a small library of patterns I reuse often.

One of those patterns is reading the previous value of state or props. It only takes a few lines and uses useRef(). A ref can point at an HTML element, but it can also store any value you assign to it.

Combined with useEffect(), that gives us a small usePrevious() hook:

tsx
import React from 'react';
export default function usePrevious<T>(value: T): T {
// Create a reference to hold the previous version of the value,
// as it is basically a generic object whose `current` property can hold any value.
const ref = React.useRef<T>();
// Use the `useEffect` hook to run a callback...
React.useEffect(() => {
// ...to store the passed value on the ref's current property...
ref.current = value;
}, [value]); // ...whenever the value changes.
// And return the currently stored value,
// as this will run before the `useEffect` callback runs.
return ref.current;
}

What to do with this hook?

When you combine usePrevious() with state, you can run logic when a value differs from the previous render. I often use it to compare boolean values, like a loading state:

tsx
import React from 'react';
const Application = () => {
// Call some external API through a custom hook.
const { data, loading } = useAsyncApi()
// Pass its loading indicator to our created hook.
const wasLoading = usePrevious<boolean>(loading);
// Use the `useEffect` hook to run a callback...
React.useEffect(() => {
// ...to check if the API was loading but now it is completed...
if (!loading && wasLoading) {
// ...and run some arbitary code...
}
}, [loading, wasLoading]); // ...whenever one of these changes.
return (
<div>
{/* ... */}
</div>
);
}

It is a small hook, but it is useful whenever the current render needs to know what changed since the previous one.