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;
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;