Skip to content

Instantly share code, notes, and snippets.

@gragland
Last active August 28, 2021 19:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • 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

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