-1

I have the code below

const [status, statusSetter] = useState({ isAuthenticated: false });

useEffect(() => {
    let didCancel = false;
    
    async function fetchMyAPI() {
        if (!didCancel) {
            let response = (await axios.get('api/auth/getuserstatus')).data;

            statusSetter(response);

        }
        
    }

    fetchMyAPI();

    return () => {
        didCancel = true;
    }
    }, []);

I have tried implementing the didcancel as a form of clean up for userEffects but it doesnt work. I get the following error: "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. " what am i supposed to put in my clean up?

ariannnn
  • 47
  • 1
  • 2
  • 8
  • this question answered here https://stackoverflow.com/questions/62126754/react-how-do-you-cleanup-useeffect-after-fetching-data-and-setting-a-state-w – İlker Mar 31 '21 at 21:30
  • Does this answer your question: https://stackoverflow.com/questions/64777668/memory-leak-on-react-app-that-uses-realtime-firebase-database/64778039#64778039 The issue is stale enclosure of `didCancel` in the effect callback. You should use an Axios cancel token to cancel any inflight requests. – Drew Reese Mar 31 '21 at 21:30

3 Answers3

1

That variable should only prevent calling statusSetter:

async function fetchMyAPI() {
        let response = (await axios.get('api/auth/getuserstatus')).data;
        !didCancel && statusSetter(response);       
}

But, basically, this is a hack, since this approach doesn't clean up anything. We keep useless background work and just don't change the component state in the result. We should abort the pending get request in some way.

With a custom experimental hook we can write auto-cancellable async routines. For example the following json request will be aborted automatically if the component unmounted while fetching (Live demo to play) :

import React, { useState } from "react";
import { useAsyncEffect, E_REASON_UNMOUNTED } from "use-async-effect2";
import cpAxios from "cp-axios";

export default function TestComponent(props) {
  const [text, setText] = useState("");

  const cancel = useAsyncEffect(function* () {
        const response = yield cpAxios(props.url);
        setText(`Success: ${JSON.stringify(response.data)}`);
    },
    [props.url]
  );

  return (...)
}
Dmitriy Mozgovoy
  • 1,419
  • 2
  • 8
  • 7
  • first approach, I went over the code and here is what happens, somehow the return method performs actions first making didCancel true so the data will never load. Can you help me with that please? – ariannnn Apr 01 '21 at 07:08
0

This code should work for you:

const [status, statusSetter] = useState({ isAuthenticated: false });
let didCancel = false;
useEffect(() => {

async function fetchMyAPI() {
    let response = (await axios.get('api/auth/getuserstatus')).data;
    if (!didCancel) {
        statusSetter(response);
    }
}

fetchMyAPI();

return () => {
    didCancel = true;
}
}, []);
mmihic
  • 21
  • 6
  • I went over the code and here is what happens, somehow the return method performs actions first making didCancel true so the data will never load. Can you help me with that please? – ariannnn Apr 01 '21 at 07:09
  • Then your component gets unmount for some reason. Can you put console.log there and see how many times it gets unmount? – mmihic Apr 01 '21 at 09:08
0

With axios, you can do it this way:

  useEffect(() => {
    const source = axios.CancelToken.source();
    fetchMyAPI(source);
    return () => {
      source.cancel();
    };
  }, []);

Then in the fetch function

const fetchMyAPI = async (source) => {
    try {
      const response = await axios({
        cancelToken: source.token,
      });
    } catch (err) {
      if (!axios.isCancel(err)) {
         // TO DO...
      }
    }
  };
Sortweste
  • 263
  • 2
  • 5
  • 12