In React 18, there is mount -> unmount -> mount in strict mode for various reasons. I am trying to place an API call in the useEffect hook and I am trying to figure out what is the best way to write a cleanup such that the API call is not triggered again when it mounts again.
function componentName(){
useEffect(() => {
fetchCall(); // Want to call this once
},[]);
return(<div></div>)
}
Using a component level state to check if a call was made does not work, because the state does not update between in this process.
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
async function fetchData(){
setIsLoading(false);
const apiResponse = await getAPI(); //gets called twice
}
if(isLoading){
fetchData();
}
},[])
I don't want to use some AbortController to abort the previous call because that is just ugly. I cannot figure out just how to tell in the mounting process that this call has been made. At first, I thought maybe there was some race condition between the state update and API request but the state is set before we are waiting for the API response.
Basically, What is the ideal way to ensure the call is triggered just once? I found solutions like using a cache in between or using react-query or some just saying to remove the strict mode but there has to be a clean way to do this.
My 2 cents:
Create a custom hook:
function useEffectively(fnToRun, deps = []) {
const refRanOnce = useRef(false);
const refDeps = useRef(deps);
useEffect(() => {
let cleanup;
const depsChanged = !isEqual(refDeps.current, deps); // isEqual can be a custom code or import it from lodash
if (!refRanOnce.current || depsChanged) {
refRanOnce.current = true;
refDeps.current = deps;
cleanup = fnToRun();
}
return () => {
// you may reset refRanOnce.current to false here, if required
cleanup?.(); // clean up for the function that you may pass, if it exists
}
}, deps);
}
This custom hook will trigger when dependencies actually change in between renders or at the time of initial mount.
Now use this custom hook for api calls:
useEffectively(fetchCall) // add dependencies if required
It may NOT cover all your use-cases, but you may modify it as per your need.