Sometimes you want to use useEffect() to catch a change in a state, but only after the initial value has been set. For example, you have a state called selectedId, and you want to do something when the value changes.

You may have defined the state as follows, with an initial value:

const [selectedId, setSelectedId] = useState<number>(0);
TypeScript

To act on changes to selectedId, you configured a useEffect() statement as follows:

useEffect(() => {
  console.log(`selectedId has changed to ${selectedId}. Or the initial value is being set.)`);
}, [selectedId]);
JavaScript

This works fine for most cases, but the effect fires on every change as well as when the state is being set to its initial value.
What if we only want to detect changes after the initial value is set?

Instead of useEffect(), we can use a custom hook:

function useDidUpdateEffect(fn, inputs) {
  const isMountingRef = useRef(false);

  useEffect(() => {
    isMountingRef.current = true;
  }, []);

  useEffect(() => {
    if (!isMountingRef.current) {
      return fn();
    } else {
      isMountingRef.current = false;
    }
  }, inputs);
}

// Replace useEffect() with useDidUpdateEffect():
useDidUpdateEffect(() => {
  console.log(`selectedId has changed to ${selectedId}. This won't fire when the initial value is being set.`);
}, [selectedId]);
JavaScript

To make it more reusable, I like to add the custom hook in a separate file and import it when needed:

useDidUpdateEffect.tsx:

import React, { useEffect, useRef } from "react";

export const useDidUpdateEffect = (fn: any, inputs: React.DependencyList | undefined) => {
  const isMountingRef = useRef(false);
  useEffect(() => {
    isMountingRef.current = true;
  }, []);
  useEffect(() => {
    if (!isMountingRef.current) {
      return fn();
    } else {
      isMountingRef.current = false;
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, inputs);
}
TypeScript

Add required import statements to myComponent.tsx:

import { useDidUpdateEffect } from "hooks/useDidUpdateEffect";

useDidUpdateEffect(() => {
  console.log(`selectedId has changed to ${selectedId}. This won't fire when the initial value is being set.`);
}, [selectedId]);
JavaScript

Source: https://stackoverflow.com/a/53180013/4669143

Published On: October 16, 2023Categories: React