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