How to post an asynchronous request in useEffect?

A component modal window. When clicking outside the modal window, it closes when you open it an asynchronous request.
handleClickOutside function(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 clicking outside the modal window, I get this error:
index.js:1 Warning: Can't perform a state update React 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 ?
April 7th 20 at 10:56
3 answers
April 7th 20 at 10:58
In your case you need to avoid update if the component state was unmounted. The most correct, in my opinion, the decision is to end the request when unmounting. Example with fetch, because I don't know if the option to cancel the request to 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]);


If we consider the example above, theoretically it should work like this:
useEffect(() => {
 let isMounted = true; 
 const fetchData = async () => {
...
 if (isMounted) {
setImg(data);
setPreloader(!preloader);
}
...
}
...
 return () => { isMounted = false; };
}, [])
April 7th 20 at 11:00
codethat allows to check whether the component is mounted into the DOM, before doing setState
const isMounted = useIsMounted();

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

prevents an error when a component died, and the query still did not work
Still error for some reason. - Kailee_Wilderm commented on April 7th 20 at 11:03
Error because useEffect() is always performed with the values of the variables state and props, which were at the time of the call effect. - halle commented on April 7th 20 at 11:06
@Jace, what the hell? In reacte there is an error that throws your condition?) I thought
Can't perform a state update React on an unmounted component. said the obvious: that suggests - orland.Wilkins commented on April 7th 20 at 11:09
April 7th 20 at 11:02
For asynchronous request processing it is better to write your hook on the basis of reutovsky. Here's an example:
import React from 'react'

const useFetch = (url, options) => {
 const [response, setResponse] = React.useState(null)
 const [error, setError] = React.useState(null)
 React.useEffect(() => {
 const fetchData = async () => {
 try {
 const res = await fetch(url)
 const json = await res.json()
setResponse(json)
 } catch (error) {
setError(error)
}
}
fetchData()
 }, [url])
 return { response, error }
}

export default useFetch


Then he very simply called in the right place:
1. Import hook:
import useFetch from '../hooks/useFetch'
2. Use the hook useFetch (for example in the component ... any where it is necessary)
const baseUrl = process.env.REACT_APP_API_URL
const apiUrl = `${baseUrl}/user/login?somedata`
const { response } = useFetch(apiUrl, { method: 'GET' })

3. If something is unclear, write, answer )
In the options you can pass anything, for example, any other data needed for the query. It is possible to rewrite under axios, I think will not cause problems ) - merritt_Kirl commented on April 7th 20 at 11:05

Find more questions by tags React