I am creating a custom hook to fetch api on form submit, I make the api call inside an useEffect hook and I have a reducer to handle the hook's states.
One of the states is trigger set to false at first which controls if the useEffect do anything, the point is the hook returns a function that flips trigger value which triggers the useEffect only when you call this function.
The problem is the useEffect's cleanup function is called during the api call even though the component is clearly still mounted.
The cleanup function seems to be fired because I set the value of trigger based on its previous value, when I set trigger to a fixed value the cleanup function is not called but I lose my functionality
const fetchReducer = (state, action) => {
switch (action.type) {
case 'FETCH_TRIGGER':
return {
...state,
trigger: !state.trigger
}
case 'FETCH_INIT':
return {
...state,
isLoading: true,
isError: false
};
case 'FETCH_SUCCESS':
return {
...state,
isLoading: false,
isError: false,
datas: action.payload,
};
case 'FETCH_FAILURE':
return {
...state,
isLoading: false,
isError: true,
};
default:
throw new Error();
}
}
const useFetchApi = (query, initialData = []) => {
let isCancelled = false;
const [state, dispatch] = useReducer(fetchReducer, {
isLoading: false,
isError: false,
datas: initialData,
trigger: false
});
const triggerFetch = _ => dispatch({ type: 'FETCH_TRIGGER' });
const cancel = _ => { console.log("canceling");isCancelled = true };
useEffect(_ => {
if (!state.trigger)
return;
triggerFetch();
(async _ => {
dispatch({ type: 'FETCH_INIT' });
try {
const datas = await query();
if (!isCancelled) { //isCancelled is true at this point
dispatch({ type: 'FETCH_SUCCESS', payload: datas })
}
} catch (err) {
if (!isCancelled) {
dispatch({ type: 'FETCH_FAILURE', payload: err })
}
}
})();
return cancel;
}, [state.trigger]);
return { ...state, triggerFetch};
}
Usage:
function MyComponent () {
const { datas, isLoading, isError, triggerFetch } = useFetchApi(query);
return (
<form onSubmit={event => {event.preventDefault(); triggerFetch()}}>
...
state.triggerchanges.cancelfunction and have it with an empty array dependency that would mimiccomponentWillUnmountlikeuseEffect(() => cancel, [])