1
1
1programmer2020-02-10 11:04:10
React
1programmer, 2020-02-10 11:04:10

How to correctly send an asynchronous request to useEffect?

There is a component, a modal window. When you click outside the modal window, it closes, when you open it, an asynchronous request flies.

function handleClickOutside(e: MouseEvent) {
    if (wrapModal.current) {
      if (e.target && !wrapModal.current.contains(e.target as Node)) {
        setIsOpen(false);
        setCurrentImg(0);
        setImg(null);
      }
    }
  }

  useEffect(() => {
    const fetchData = async () => {
      await axios.get(url).then((data: any) => {
        setImg(data);
        setPreloader(!preloader);
      });
    };
    fetchData();
  }, [url]);

  useEffect(() => {
    document.addEventListener("mousedown", handleClickOutside, false);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside, false);
    };
  }, []);


Now when I click outside of the modal, I get an error:
index.js:1 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 a useEffect cleanup function.

How to fix it?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
T
twolegs, 2020-02-10
@twolegs

In your case, you need to avoid updating the state if the component has been unmounted. The most correct, in my opinion, solution is to cancel the request when unmounting. fetch example, because I don't know if there is an option to cancel the request in axios.

useEffect(() => {
    const abortController = new AbortController();
    const fetchData = async () => {
      await fetch(url, { abortController }).then((data: any) => {
        setImg(data);
        setPreloader(!preloader);
      });
    };
    fetchData();

    return () => {
      abortController.abort(); 
    }
  }, [url]);

Considering the example above, theoretically this should work:
useEffect(() => {
  let isMounted = true; 
  const fetchData = async () => {
   ...
    if (isMounted) {
      setImg(data);
      setPreloader(!preloader);
    }
   ...
  }
  ...
  return () => { isMounted = false; };
}, [])

P
Petr Muhurov, 2020-02-10
@pterodaktil

code that allows you to check if the component is mounted in the DOM before doing setState

const isMounted = useIsMounted();

useEffect(() => {
...
  if (isMounted()) {
    setImg(data);
    setPreloader(!preloader);
  }
...
}, [])

allows you to prevent an error when the component has died, and the request has not yet completed

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question