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; | |
} |
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;
}
hmmm why would you need an initialValue for a usePrevious? it was undefined previously.
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
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.
@manzoorwanijk can you give some example code?
…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}/>
}
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];
}
@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;
}
Because technically it had no previous value on the first render and you may need to know that in your components.