Quando você dispara uma promessa, pode levar alguns segundos antes que ela seja resolvida e, nessa altura, o usuário pode ter navegado para outro local em seu aplicativo. Portanto, quando o Promise resolve setStateé executado em um componente não montado e você obtém um erro - como no seu caso. Isso também pode causar vazamentos de memória.
É por isso que é melhor remover parte de sua lógica assíncrona dos componentes.
Caso contrário, você precisará cancelar sua promessa de alguma forma . Alternativamente - como técnica de último recurso (é um antipadrão) - você pode manter uma variável para verificar se o componente ainda está montado:
componentDidMount(){
this.mounted = true;
this.props.fetchData().then((response) => {
if(this.mounted) {
this.setState({ data: response })
}
})
}
componentWillUnmount(){
this.mounted = false;
}
Vou enfatizar isso novamente - este é um antipadrão, mas pode ser suficiente no seu caso (assim como fizeram com a Formikimplementação).
Uma discussão semelhante no GitHub
EDITAR:
Provavelmente é assim que eu resolveria o mesmo problema (não tendo nada além de React) com Ganchos :
OPÇÃO A:
import React, { useState, useEffect } from "react";
export default function Page() {
const value = usePromise("https://something.com/api/");
return (
<p>{value ? value : "fetching data..."}</p>
);
}
function usePromise(url) {
const [value, setState] = useState(null);
useEffect(() => {
let isMounted = true;
request.get(url)
.then(result => {
if (isMounted) {
setState(result);
}
});
return () => {
isMounted = false;
};
}, []);
return value;
}
OPÇÃO B: Alternativamente, com o useRefqual se comporta como uma propriedade estática de uma classe, o que significa que não faz o componente rerender quando seu valor muda:
function usePromise2(url) {
const isMounted = React.useRef(true)
const [value, setState] = useState(null);
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
request.get(url)
.then(result => {
if (isMounted.current) {
setState(result);
}
});
}, []);
return value;
}
function useIsMounted() {
const isMounted = React.useRef(true)
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
return isMounted;
}
Exemplo: https://codesandbox.io/s/86n1wq2z8