【发布时间】:2019-07-31 05:13:08
【问题描述】:
如果我们想限制useEffect仅在组件挂载时运行,我们可以在[]中添加useEffect的第二个参数。
useEffect(() => {
// ...
}, []);
但是我们怎样才能让useEffect只在组件更新的那一刻才运行呢?
【问题讨论】:
-
嗯..下面的解决方案看起来更精确和清晰。
标签: reactjs react-hooks
如果我们想限制useEffect仅在组件挂载时运行,我们可以在[]中添加useEffect的第二个参数。
useEffect(() => {
// ...
}, []);
但是我们怎样才能让useEffect只在组件更新的那一刻才运行呢?
【问题讨论】:
标签: reactjs react-hooks
如果您希望 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
}
});
【讨论】:
useRef,然后从react-use 使用useUpdateEffect。
我真的很喜欢 Shubham 的回应,所以我把它做成了一个自定义 Hook
/**
* 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);
}
【讨论】:
return effect(); 而不是仅仅调用effect(); 以便我们尊重清理功能吗?
Shubham 和 Mario 都提出了正确的方法,但是代码仍然不完整,没有考虑以下情况。
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
};
【讨论】:
isInitialMount.current = true;?真的有必要吗?
您可以通过将状态设置为非布尔初始值(如空值)来解决它:
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。
从 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.log('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
...
})
}
【讨论】:
要使自定义钩子符合钩子规则,您不需要实际传递依赖项,只需使用 useCallback 包装您的效果函数
function useEffectOnUpdate(callback) {
const mounted = useRef();
useEffect(() => {
if (!mounted.current) {
mounted.current = true;
} else {
callback();
}
}, [callback]);
};
function SomeComponent({ someProp }) {
useEffectOnUpdate(useCallback(() => {
console.log(someProp);
}, [someProp]));
return <div>sample text</div>;
}
【讨论】:
使用useEffect 的清理功能,而不使用空数组作为第二个参数:
useEffect(() => {
return () => {
// your code to be run on update only.
}
});
【讨论】: