Перехватчики реакции используют эффект только при обновлении?

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

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

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

Ну... решение ниже выглядит более точным и понятным.

koo 25.06.2019 08:13
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
84
2
48 160
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Ответ принят как подходящий

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

const isInitialMount = useRef(true);

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

Я думал, что useRef можно использовать только для манипуляций с DOM. Спасибо!

koo 09.03.2019 16:02

очень умное решение

Mohamed Tajjiou 12.03.2019 12:32

На самом деле этот вопрос указан в Ответить на часто задаваемые вопросы и здесь здесь прямо сказано, что это правильный путь.

Daniel 10.11.2019 16:26

Это оно. Я предлагаю сначала понять useRef, а затем использовать useUpdateEffect из react-use.

adi518 27.02.2020 15:29

Просто и очень полезно :)

Ali Rehman 28.10.2020 12:22

Отличное решение.

Mahesh Talaviya 30.01.2021 07:02

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

/**
 * A custom useEffect hook that only triggers on updates, not on initial mount
 * @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 {
      return effect();
    }
  }, dependencies);
}

Почему вы отключаете правило eslint?

zeckdude 27.11.2019 22:23

Я не смог найти способ сделать его совместимым с правилом, поэтому отключил его. Я уберу это из ответа, так как не имеет значения.

Mario Campa 03.12.2019 20:51

Для машинописного текста мне пришлось изменить на `useUpdateEffect (эффект: функция, зависимости: любой [] = [])`

Rotem 28.05.2020 11:00

Разве мы не должны return effect(); вместо того, чтобы просто вызывать effect();, чтобы выполнить функцию очистки?

Code Commander 23.12.2020 20:24

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

  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
};

Здорово. Почему мы использовали useIsMounted()? Почему мы не должны использовать if (!isInitialMount.current) при очистке?

Sarat Chandra 23.03.2020 12:58

Зачем нам нужно ставить isInitialMount.current = true; при уборке? Это действительно необходимо?

Code Commander 23.12.2020 20:24

Другим подходом может быть вызов useEffect дважды или более, в зависимости от «проблемы». См. ответ Руди Наппе из комментариев ниже сообщения в блоге:

https://dev.to/savagepixie/how-to-mimic-componentdidupdate-with-react-hooks-3j8c

В двух словах:

useEffect(concern1, [...]);
useEffect(concern2, [...]);
...

Для каждого экземпляра useEffect передайте другой аргумент массива, который вы пытаетесь просмотреть.

Вы можете обойти это, установив для состояния нелогическое начальное значение (например, нулевое значение):

  const [isCartOpen,setCartOpen] = useState(null);
  const [checkout,setCheckout] = useState({});

  useEffect(() => {

    // check to see if its the initial state
    if ( isCartOpen === null ){

      // first load, set cart to real initial state, after load
      setCartOpen( false );
    }else if (isCartOpen === false){

      // normal on update callback logic
      setCartOpen( true );
    }
  }, [checkout]);

Этот код не будет работать, потому что isCartOpen не соблюдается и всегда будет null.

Kevin Ghadyani 01.04.2020 22:57

Принял помощь от ответа Subham Этот код будет выполняться только для обновления конкретного элемента, а не при каждом обновлении и не при первоначальном монтировании компонента.

const isInitialMount = useRef(true);    //useEffect to run only on updates except initial mount


//useEffect to run only on updates except initial mount
  useEffect(() => {
    if (isInitialMount.current) {
        isInitialMount.current = false;
     } else {              
         if (fromScreen!='ht1' && appStatus && timeStamp){
            // let timeSpentBG = moment().diff(timeStamp, "seconds");
            // let newHeatingTimer = ((bottomTab1Timer > timeSpentBG) ? (bottomTab1Timer - timeSpentBG) : 0);
            // dispatch({
            //     type: types.FT_BOTTOM_TAB_1,
            //     payload: newHeatingTimer,
            // })
            // console.info('Appstaatus', appStatus, timeSpentBG, newHeatingTimer)
         }
     }
  }, [appStatus])

Короче один

const [mounted, setMounted] = useRef(false)

useEffect(() => {
  if (!mounted) return setMounted(true)
  ...
})

Реагировать на хуки

Крюк

export const useMounted = () => {
  const mounted = useRef(false)

  useEffect(() => {
    mounted.current = true
    return () => {
      mounted.current = false
    }
  }, [])

  return () => mounted.current
}

Применение

const Component = () => {
  const mounted = useMounted()

  useEffect(() => {
    if (!mounted()) return
    ...
  })
}

Разве возврат не используется для очистки при размонтировании?

user2078023 02.12.2021 20:04

Используйте Функция очисткиuseEffect без использования пустого массива в качестве второго параметра:

useEffect(() => { 
  return () => {
  // your code to be run on update only.
  }
});

You can use another useEffect (with an empty array as a second parameter) for initial mount, where you place your code in its main function.

Я получаю бесконечный цикл

Isaac Pak 15.08.2021 20:57

Функция очистки реакции выполняется, когда компонент размонтируется. ОП спрашивает, как выполнять код при «обновлении» или, другими словами, при каждом рендеринге, кроме начального рендеринга, который запускается при монтировании.

lance.dolan 04.10.2021 05:26

Чтобы сделать пользовательский хук совместимым с правилами хуков, вам не нужно фактически передавать зависимости, просто оберните функцию эффекта с помощью useCallback

function useEffectOnUpdate(callback) {
  const mounted = useRef();

  useEffect(() => {
    if (!mounted.current) {
      mounted.current = true;
    } else {
      callback();
    }
  }, [callback]);
};

function SomeComponent({ someProp }) {
  useEffectOnUpdate(useCallback(() => {
    console.info(someProp);
  }, [someProp]));

  return <div>sample text</div>;
}

Другие вопросы по теме