Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
import { useState, useEffect, useRef } from 'react';
// Usage
function App() {
// State value and setter for our example
const [count, setCount] = useState(0);
// Get the previous value (was passed into hook on last render)
const prevCount = usePrevious(count);
// Display both current and previous count value
return (
<div>
<h1>Now: {count}, before: {prevCount}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
// Hook
function usePrevious(value) {
// The ref object is a generic container whose current property is mutable ...
// ... and can hold any value, similar to an instance property on a class
const ref = useRef();
// Store current value in ref
useEffect(() => {
ref.current = value;
}, [value]); // Only re-run if value changes
// Return previous value (happens before update in useEffect above)
return ref.current;
}
@ianobermiller

This comment has been minimized.

Copy link

@ianobermiller ianobermiller commented Nov 9, 2018

Love all the comments in the custom hook function!

@ritwickdey

This comment has been minimized.

Copy link

@ritwickdey ritwickdey commented Nov 14, 2018

Just an optimization, not sooooo inportant

useEffect(() => {
    ref.current = value;
  }, [value]); // only run if and only if value is changed
@gragland

This comment has been minimized.

Copy link
Owner Author

@gragland gragland commented Nov 20, 2018

@ritwickdey Thanks, code updated!

@olee

This comment has been minimized.

Copy link

@olee olee commented Dec 30, 2018

I wrote the following alternative which (even if it looks a bit complicated) can get around with just a single useState and should increase performance a lot because it does not need to check if the effect needs to run on every render.

import { useState, Dispatch, SetStateAction } from 'react';

type SetStateFn<T> = (previousValue: T) => T;

type IValueWrapper<T> = [
    /** value */
    T,
    /** setValue */
    Dispatch<SetStateAction<T>>,
    /** previousValue */
    T | undefined
];

/**
 * Drop-in replacement for react useState which also returns previousValue.
 * @return [value, setValue, previousValue]
 */
export default function useStateWithPrevious<T>(initialValue: T) {
    const createSetStateDispatch: (previousValue: T) => Dispatch<SetStateAction<T>> = previousValue => value => {
        const newValue = typeof value === 'function' ? (value as SetStateFn<T>)(previousValue) : value;
        setValueWrapper([
            newValue,
            createSetStateDispatch(newValue),
            previousValue,
        ]);
    };
    const [valueWrapper, setValueWrapper] = useState<IValueWrapper<T>>(() => [
        initialValue,
        createSetStateDispatch(initialValue),
        undefined,
    ]);
    return valueWrapper;
}
@thebiltheory

This comment has been minimized.

Copy link

@thebiltheory thebiltheory commented Jun 11, 2019

Clean up

function usePrevious(value) {
  // The ref object is a generic container whose current property is mutable ...
  // ... and can hold any value, similar to an instance property on a class
  const ref = useRef();
  
  // Store current value in ref
  useEffect(() => {
    ref.current = value;
    // Clean Up
    return () => {
          ref.current = null;
    }
  }, [value]); // Only re-run if value changes

(cc @AmaanKulshreshtha )

@PutziSan

This comment has been minimized.

Copy link

@PutziSan PutziSan commented Jul 26, 2019

The function doesn't quite do what I expected it to do. If another state changes and renders the component again, ref.current === value applies.
I have adapted the function so that it reacts as I expect it to:

function usePrevious(value) {
  const ref = useRef({ v: value, prev: undefined });

  // update `ref` only, if the value really changes,
  // the previous value is kept persistent in `prev`,
  // so it also contains the previous value for
  // further renders with the same `value`.
  if (ref.current.v !== value) {
    ref.current = {
      prev: ref.current.v, 
      v: value 
    };
  }

  return ref.current.prev;
}
@clarkkozak

This comment has been minimized.

Copy link

@clarkkozak clarkkozak commented May 29, 2020

import { useState, useEffect, useRef } from 'react';
to
import React, { useState, useEffect, useRef } from 'react';

@deadlyicon

This comment has been minimized.

Copy link

@deadlyicon deadlyicon commented Jun 11, 2020

Any reason why we need the useEffect? Would this be cheaper?:

function usePrevious(value){
  const ref = useRef()
  const previous = ref.current
  ref.current = value
  return previous
}
@gragland

This comment has been minimized.

Copy link
Owner Author

@gragland gragland commented Aug 6, 2020

@thebiltheory Why is cleanup necessary? Haven't seen any examples that set a ref value back to null on cleanup and it's not mentioned in https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state

@PutziSan That should be handled by the dependency array no?

@deadlyicon @PutziSan From what I've read it's not concurrent mode safe to update a ref in the body like that, so could cause bugs in the future when concurrent mode is used.

@deadlyicon

This comment has been minimized.

Copy link

@deadlyicon deadlyicon commented Aug 16, 2020

Good to know. Thanks @gragland

@fix777

This comment has been minimized.

Copy link

@fix777 fix777 commented Sep 14, 2020

It's a fancy hook, but I'm wondering why is the ref not been initialized at the the 1st rendering.

export default function usePrevious(value) {
  const ref = useRef(value);

  // ...
}

Would this be better?

@gragland

This comment has been minimized.

Copy link
Owner Author

@gragland gragland commented Sep 19, 2020

It's a fancy hook, but I'm wondering why is the ref not been initialized at the the 1st rendering.

Because technically it had no previous value on the first render and you may need to know that in your components.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.