When you fire a Promise it might take a few seconds before it resolves and by that time user might have navigated to another place in your app. So when Promise resolves
setState
is executed on unmounted component and you get an error - just like in your case. This may also cause memory leaks.
That's why it is best to move some of your asynchronous logic out of components.
Otherwise, you will need to somehow cancel your Promise. Alternatively - as a last resort technique (it's an antipattern) - you can keep a variable to check whether the component is still mounted:
componentDidMount(){
this.mounted = true;
this.props.fetchData().then((response) => {
if(this.mounted) {
this.setState({ data: response })
}
})
}
componentWillUnmount(){
this.mounted = false;
}
I will stress that again - this is an antipattern but may be sufficient in your case (just like they did with
Formik
implementation).
A similar discussion on GitHub
EDIT:
This is probably how would I solve the same problem (having nothing but React) with upcoming Hooks:
import React, { useState, useEffect } from "react";
export default function Page() {
const value = usePromise("https://something.com/api/");
return (
<p>{value ? value : "fetching data..."}</p>
);
}
let isMounted; // track whether component is mounted
function usePromise(url) {
const [value, setState] = useState(null);
useEffect(() => {
isMounted = true;
request.get(url)
.then(result => {
if (isMounted) {
setState(result);
}
});
return () => {
// clean up
isMounted = false;
};
}, []); // only on "didMount"
return value;
}
Example: https://codesandbox.io/s/86n1wq2z8
from stackoverflow: https://stackoverflow.com/questions/49906437/how-to-cancel-a-fetch-on-componentwillunmount