If we want to restrict useEffect
to run only when the component mounts, we can add second parameter of useEffect
with []
.
Use the Cleanup function of the useEffect
without using an empty array as a second parameter:
useEffect(() => {
return () => {
// your code to be run only on update
}
});
If you wish, you can use another useEffect (with an empty array as a second parameter) for initial mount, where you place the code in its main function as usual.
Took help from Subham's answer This code will only run for particular item update not on every update and not on component initial mounting.
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)
// your update codes goes here
});
I really like Shubham's response, so I made it a custom 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);
}
Another approach would be to call useEffect twice or more, depending on the 'concern'. Please see Rudy Nappée's answer from the comments down below the blog post:
https://dev.to/savagepixie/how-to-mimic-componentdidupdate-with-react-hooks-3j8c
In a nutshell:
useEffect(concern1, [...]);
useEffect(concern2, [...]);
...
For each instance of useEffect, pass a different array argument you are trying to watch.
Both Shubham and Mario suggest the right approach, however the code is still incomplete and does not consider following cases.
effect
function may have a cleanup function returned from it, that would never get called Sharing below a more complete code which covers above two missing cases:
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
};