React 挂钩仅在更新时使用 Effect?
React hooks useEffect only on update?
如果我们想限制useEffect
为运行只有当组件挂载时,我们可以添加useEffect
的第二个参数[]
。
useEffect(() => {
// ...
}, []);
但是如何让useEffect
变成运行只在组件更新的时候,除了初始挂载?
如果您希望 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
}
});
我非常喜欢 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);
}
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
};
您可以通过将状态设置为非布尔初始值(如空值)来绕过它:
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]);
从 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)
...
})
React Hook 解决方案
挂钩
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
...
})
}
使用 useEffect
的 Cleanup 函数,而不使用空数组作为第二个参数:
useEffect(() => {
return () => {
// your code to be run on update only.
}
});
您可以使用另一个 useEffect(第二个参数为空数组)进行初始挂载,将代码放在其主要函数中。
要使自定义钩子符合钩子规则,您不需要实际传递依赖项,只需使用 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
的第二个参数[]
。
useEffect(() => {
// ...
}, []);
但是如何让useEffect
变成运行只在组件更新的时候,除了初始挂载?
如果您希望 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
}
});
我非常喜欢 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);
}
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
};
您可以通过将状态设置为非布尔初始值(如空值)来绕过它:
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]);
从 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)
...
})
React Hook 解决方案
挂钩
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
...
})
}
使用 useEffect
的 Cleanup 函数,而不使用空数组作为第二个参数:
useEffect(() => {
return () => {
// your code to be run on update only.
}
});
您可以使用另一个 useEffect(第二个参数为空数组)进行初始挂载,将代码放在其主要函数中。
要使自定义钩子符合钩子规则,您不需要实际传递依赖项,只需使用 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>;
}