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
И Шубхам, и Марио предлагают правильный подход, однако код все еще не завершен и не учитывает следующие случаи.
- Если компонент размонтируется, он должен сбросить его флаг
- Передающая функция
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
};