React-хуки используют, влияют только на обновление?

Если мы хотим ограничить использование useEffect только при монтировании компонента, мы можем добавить второй параметр useEffect с помощью [].

useEffect(() => {
  // ...
}, []);

Но как мы можем использовать useEffect для запуска только тогда, когда обновляется компонент, кроме первоначального монтирования?

Ответ 1

Если вы хотите запустить useEffect для запуска только для обновлений, кроме первоначального монтирования, вы можете использовать useRef для отслеживания initialMount с useEffect без второго параметра.

const isInitialMount = useRef(true);

useEffect(() => {
  if (isInitialMount.current) {
     isInitialMount.current = false;
  } else {
      // Your useEffect code here to be run on update
  }
});

Ответ 2

Мне очень нравится ответ шубхэма, поэтому я сделал его кастомным Hook

/**
 * A custom useEffect hook that only triggers on updates, not on initial mount
 * Idea stolen from: https://stackoverflow.com/a/55075818/1526448
 * @param {Function} effect
 * @param {Array<any>} dependencies
 */
export default function useUpdateEffect(effect, dependencies = []) {
  const isInitialMount = useRef(true);

  useEffect(() => {
    if (isInitialMount.current) {
      isInitialMount.current = false;
    } else {
      effect();
    }
  }, dependencies); // eslint-disable-line react-hooks/exhaustive-deps
}

Ответ 3

И Шубхам, и Марио предлагают правильный подход, однако код все еще не завершен и не учитывает следующие случаи.

  1. Если компонент размонтируется, он должен сбросить его флаг
  2. Передающая функция effect может иметь возвращаемую функцию очистки, которая никогда не будет вызвана

Ниже приведен более полный код, охватывающий два пропущенных случая:

import React from 'react';

const useIsMounted = function useIsMounted() {
  const isMounted = React.useRef(false);

  React.useEffect(function setIsMounted() {
    isMounted.current = true;

    return function cleanupSetIsMounted() {
      isMounted.current = false;
    };
  }, []);

  return isMounted;
};

const useUpdateEffect = function useUpdateEffect(effect, dependencies) {
  const isMounted = useIsMounted();
  const isInitialMount = React.useRef(true);

  React.useEffect(() => {
    let effectCleanupFunc = function noop() {};

    if (isInitialMount.current) {
      isInitialMount.current = false;
    } else {
      effectCleanupFunc = effect() || effectCleanupFunc;
    }
    return () => {
      effectCleanupFunc();
      if (!isMounted.current) {
        isInitialMount.current = true;
      }
    };
  }, dependencies); // eslint-disable-line react-hooks/exhaustive-deps
};