While I can't precisely tell you when useEffect's unsubscribe function is called more specifically than "when the component is unmounted", I can answer your question about the implications of when it is called:
is it possible for the promise to be resolved in between the time when the component unmounts and cleanup is called, causing an error, that is, accessing the state that has been deallocated?
In the code as written, it is possible for the Promise to resolve after the component unmounts. Your isApiSubscribed flag is a way of hiding the warning that might be displayed when this occurs in development mode, but it's not the correct way of dealing with this scenario.
When this happens when react is running in development mode, you might get a warning message like this:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
The point of this warning message is that you should take care to stop doing work that's no longer relevant after a particular component is unmounted. Not calling setData does prevent you from updating the state, but the work of the HTTP request will still always be completed, even if the component is unmounted. That's the bit that React is telling you that you should consider cancelling.
The "correct" way of altering this pattern to avoid spending unnecessary resources would be to stop the execution of the HTTP request when the component unmounts, which will cause the promise to reject:
useEffect(() => { const abortController = new AbortController(); axios.get(API, { signal: abortController.signal }).then((res) => { setData(res.data) }); return () => { abortController.abort(); }; }, []);
This ensures that not only do you not call setData on a component that's unmounted (which is a relatively trivial amount of work), but that you cancel the much larger piece of work which is a HTTP request (and associated response handling) when the component is unmounted.
It is theoretically possible that the following series of events could happen:
- Task starts
- Task completes
- Promise resolves
- Component unmounts
- Next event tick triggers and the continuation passed to then() is called, calling setData(), triggering the warning message
But this requires precise timing and the spirit of the warning message is to prevent the much more common scenario:
- Task starts
- Component unmounts
- Task completes
- Promise resolves
- Next event tick triggers