Chapter 4 of 4
Refs in Practice
Two patterns worth keeping, and the mistakes to avoid.
Remembering the previous value
function usePrevious(value) {
const ref = useRef(undefined);
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current; // the value from the previous render
}
function Price({ amount }) {
const previous = usePrevious(amount);
const hasRisen = previous !== undefined && amount > previous;
return <span className={hasRisen ? "up" : ""}>{amount}</span>;
}Keeping a callback fresh without re-subscribing
When an effect should not re-run but needs the latest version of a callback, park the callback in a ref.
function useInterval(callback, delay) {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
const id = setInterval(() => savedCallback.current(), delay);
return () => clearInterval(id);
}, [delay]); // the interval only resets when the delay changes
}Mistakes to avoid
- Reading
ref.currentduring render. It may benull, and reading it makes the render impure. - Writing to a ref during render for the same reason - do it in an effect or a handler.
- Using a ref for something the UI displays. That is state.
- Using a ref to avoid a dependency warning. Fix the dependency instead.