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()}}>
...
A local variable can be used inside the useEffect callback. Credit to @gaearon
https://codesandbox.io/s/k0lm13kwxo
useEffect(() => {
let ignore = false;
async function fetchData() {
const result = await axios('https://hn.algolia.com/api/v1/search?query=' + query);
if (!ignore) setData(result.data);
}
fetchData();
return () => { ignore = true; }
}, [query]);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With