DEV Community

Andreas Riedmüller
Andreas Riedmüller

Posted on • Updated on

usePrevious + useEffect?

Sometimes I need to run a side effect in React when a certain value in the state changes.

I do this quite often and found that for me the most readable (and understandable) method is to use a hook that stores the previous value of something.

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

Then when I want to create a side effect that only runs when when a specific variable changes:

const [step, setStep] = useState(1);
const [something, setSomething] = useState('Hi world!');
const prevStep = usePrevious(step);

useEffect(() => {
  if (prevStep !== 4 && step === 4) {
    // Do something
  }
}, [step, prevStep, something]);
Enter fullscreen mode Exit fullscreen mode

Does anyone else do it like this way? How often do you need this pattern?

Example

This is one example that I found in a Upload component where I wanted to implement the upload directly in the component. This can be done in a much cleaner way with eg. Redux-Saga

There is a callback onUploaded that should be called when the upload is done.

useEffect(() => {
  if (progress === null) {
    setProgress(0); // This marks the upload as started
    uploadBlob(blob, setProgress).then(() => {
      setBlobUploaded(true);
      onUploaded(); // Dangerous: This might be out of date by the time it's called
    }).catch(reason => {
      setUploadFailedReason(reason);
    });
  }
}, [blob, progress, onUploaded]);
Enter fullscreen mode Exit fullscreen mode
useEffect(() => {
  if (progress === null) {
    setProgress(0); // This marks the upload as started
    uploadBlob(blob, setProgress).then(() => {
      setBlobUploaded(true);
    }).catch(reason => {
      setUploadFailedReason(reason);
    });
  }
}, [blob, progress]);


useEffect(() => {
  if (prevIsBlobUploaded === false && isBlobUploaded === true) {
    onUploaded(); // This is safer, but it's not clear what the state of the blob is
  }
}, [isBlobUploaded, prevIsBlobUploaded, onBlobUploaded]);
Enter fullscreen mode Exit fullscreen mode

Top comments (0)