Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cancelling useEffect's Async with TypeScript, the correct way

I have this simple example that's giving me grief:

useEffect(() => {
  axios.get(...).then(...).catch(...)

}, [props.foo])

warning: can't perform a react state update on an unmounted component

Done some research and this one is more understandable. TypeScript seems not to like that approach as useEffect should return a void.

useEffect(() => {
  let isSubscribed = true

  axios.get(...).then(...).catch(...)

  return () => (isSubscribed = false)

}, [props.foo])

TypeScript:

/**
     * Accepts a function that contains imperative, possibly effectful code.
     *
     * @param effect Imperative function that can return a cleanup function
     * @param deps If present, effect will only activate if the values in the list change.
     *
     * @version 16.8.0
     * @see https://reactjs.org/docs/hooks-reference.html#useeffect
     */
    function useEffect(effect: EffectCallback, deps?: DependencyList): void;

How to implement an isSubscribed in my useEffect with TS?

Thanks.

like image 936
Sylar Avatar asked Oct 16 '25 13:10

Sylar


1 Answers

useEffect itself returns void, but the function provided to useEffect is typed as EffectCallback. This is definied as:

// NOTE: callbacks are _only_ allowed to return either void, or a destructor.
// The destructor is itself only allowed to return void.
type EffectCallback = () => (void | (() => void | undefined));

Source

This means your effect callback can actually return a function, which has to return void or undefined.

Now you can solve your issue to avoid calling setState with the isSubscribed variable. But another (maybe better) way is to outright cancel the request.

useEffect(() => {
  const CancelToken = axios.CancelToken;
  const source = CancelToken.source();

  axios.get('...', { cancelToken: source.token }).then(/**/).catch(e => {
    if (axios.isCancel(thrown)) {
      console.log('Request canceled', thrown.message);
    } else {/* handle error */}
  });

  return () => source.cancel();
}, [props.foo])

This is also documented in the README.

The problem with your current code is, that your destructor function returns the boolean isSubscribed. Instead of returning it, just put the assignment into a function body:

return () => {
  isSubscribed = false;
}
like image 113
ChrisG Avatar answered Oct 19 '25 04:10

ChrisG



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!