DEV Community

Chris Milson
Chris Milson

Posted on

Problems with usePrevious.

Recently, I was creating a react component and I wanted to remember the previous value of one of the props. I did a google search and found several different articles[1][2][3], all trying to convince me that the following code would serve my needs. This snippet is from react's documentation:

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

The idea with this pattern is that the useEffect hook will fire its effect whenever the value variable changes. Since the effect fires after the function returns, the hook will return whatever value was the last time the hook was called not the previous value.

This means that in my component, instead of getting the previous value of a prop, I get the value of the prop on the previous render. Consider the following component.

function Counter(props) {
  const { style } = props;
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  return (
    <h1 style={style}>
      Now: {count}, before: {prevCount}
    </h1>
  );
}

If the style prop changes, it will cause an update to the component, and prevCount will suddenly display the same value as the current count. According to the react docs team this is intended behaviour. However if you are like me, consider using the following code instead:

function usePrevious(value, initial?) {
  const targetRef = useRef(value);
  const previousRef = useRef(initial);

  if (targetRef.current !== value) {
    // The value changed.
    previousRef.current = targetRef.current;
    targetRef.current = value;
  }

  return previousRef.current;
}

or with a singe ref

function usePrevious(value, initial?) {
  const ref = useRef({target: value, previous: initial});

  if (ref.current.target !== value) {
    // The value changed.
    ref.current.previous = ref.current.target;
    ref.current.target = value;
  }

  return ref.current.previous;
}

Top comments (1)

Collapse
 
gadi_tz profile image
Gadi Tzkhori

Is it safe to set ref outside useEffect? the docs says different: "Unless you’re doing lazy initialization, avoid setting refs during rendering"