I get this warning when i use my fetch post method how can I cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.With my Post methods.
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.
import React from "react"; import { useHistory } from "react-router-dom"; import { UPLOAD_PRESET, CLOUD_NAME, SERVER_API } from "../../config"; const uploadImage = async (file) => { const url = `https://api.cloudinary.com/v1_1/${CLOUD_NAME}/upload`; const formData = new FormData(); formData.append("file", file); formData.append("upload_preset", UPLOAD_PRESET); const res = await fetch(url, { method: "POST", body: formData, }); if (!res.ok) { throw new Error(`Can't upload image. ${res.status}`); } const data = await res.json(); return await data.eager[0].secure_url; }; const createAlbum = async (data) => { const res = await fetch(`${SERVER_API}/api/v1/albums`, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json", }, }); if (!res.ok) { throw new Error(`An error has occurred: ${res.status}`); } const json = await res.json(); return json.data._id; }; const Form = ({ file, loading, setError, album, color, children }) => { let history = useHistory(); const clearError = () => setError(""); const handleSubmit = async (e) => { e.preventDefault(); clearError(); try { if (!file) { throw new Error("Please select a file to add."); } if (!album.trim("") || !color.trim()) { throw new Error("Please enter all the field values."); } loading(true); const fileUrl = await uploadImage(file); const data = { name: album, bckImgUrl: fileUrl, color: color, }; const albumId = await createAlbum(data); history.push(`/albums/${albumId}`); } catch (error) { setError(error.message); } finally { loading(false); } }; return <form onSubmit={handleSubmit}>{children}</form>; }; export default Form;
Advertisement
Answer
I agree with Ramesh on using a ref. I figured I’d show how it could be extracted out into a custom hook.
function useHasUnmountedRef() { const hasUnmountedRef = useRef(false); useEffect(() => { return () => { hasUnmountedRef.current = true; } }, []); return hasUnmountedRef; } function Form() { const hasUnmountedRef = useHasUnmountedRef(); const handleSubmit = async () => { await asyncStuff(); if (hasUnmountedRef.current) { // escape early because component has unmounted return; } // Thanks to the guard clause above, you can guarantee at this // point that your component is still mounted. You can perform // state updates without generating a React warning. // // If you do another "await" however, you will need to check // again. Everytime you await something async, there is a chance // that the component could have unmounted while waiting for the // async stuff to complete. }; return ( <form onSubmit={handleSubmit} /> ); } export default Form;