Skip to content

Instantly share code, notes, and snippets.

@gragland
Last active August 28, 2021 19:25
Show Gist options
  • Save gragland/1ed713a68c770ea414c3b92ccf2bdd2f to your computer and use it in GitHub Desktop.
Save gragland/1ed713a68c770ea414c3b92ccf2bdd2f to your computer and use it in GitHub Desktop.
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;
}
@jaredatron
Copy link

Good to know. Thanks @gragland

@fix777
Copy link

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
Copy link
Author

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.

@manzoorwanijk
Copy link

A better version with support for initialValue. Backward compatible with previous version.

function usePrevious<T>(value: T, initialValue?: T): T {
	const ref = useRef(initialValue);

	useEffect(() => {
		ref.current = value;
	}, [value]);

	return ref.current;
}

@jaredatron
Copy link

hmmm why would you need an initialValue for a usePrevious? it was undefined previously.

@jaredatron
Copy link

jaredatron commented Jul 28, 2021

This seems to handle all the cases discussed in this thread*:

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

* except for initialValue

@manzoorwanijk
Copy link

manzoorwanijk commented Jul 29, 2021

hmmm why would you need an initialValue for a usePrevious? it was undefined previously.

I have a side effect which calls an API if the current value changes and is different from the previous value. Without initial value, the change is always triggered after first render.

@jaredatron
Copy link

@manzoorwanijk can you give some example code?

@jaredatron
Copy link

…are you doing something like?:

function Image({ src, ...props }){
  const prevSrc = usePrevious(src)
  useEffect(
    ()=> {
      console.log(`src changed`, {to: src, was: prevSrc})
    },
    [src]
  )
  return <img {...props} src={src}/>
}

@manikanta-kotha
Copy link

export default function usePrevious(value) {
  // ref value will always be like [prev, state]
  let ref = useRef([null, null]);

  // storing prev, state values
  ref.current.shift()
  ref.current.push(value)
  // Return previous value
  return ref.current[0];
}

@jaredatron
Copy link

@manikanta-kotha it seems like this could be simpler and work the same:

export default function usePrevious(value) {
  const ref = useRef();
  const prev = ref.current
  ref.current = value
  return prev;
}

…but it might be nice to always return the previous value. Even on subsequent renders. Like this:

export default function usePrevious(value) {
  const ref = useRef();
  if (ref.current !== value){
    ref.previous = ref.current
    ref.current = value
  }
  return ref.previous;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment