Cuando dispara una Promise, pueden pasar unos segundos antes de que se resuelva y, en ese momento, el usuario podría haber navegado a otro lugar en su aplicación. Entonces, cuando Promise resuelve setState
se ejecuta en un componente desmontado y obtiene un error, como en su caso. Esto también puede provocar pérdidas de memoria.
Por eso es mejor sacar parte de su lógica asincrónica de los componentes.
De lo contrario, deberá cancelar de alguna manera su Promesa . Alternativamente, como técnica de último recurso (es un antipatrón), puede mantener una variable para verificar si el componente aún está montado:
componentDidMount(){
this.mounted = true;
this.props.fetchData().then((response) => {
if(this.mounted) {
this.setState({ data: response })
}
})
}
componentWillUnmount(){
this.mounted = false;
}
Lo enfatizaré nuevamente: este es un antipatrón, pero puede ser suficiente en su caso (al igual que lo hicieron con la Formik
implementación).
Una discusión similar en GitHub
EDITAR:
Probablemente así es como resolvería el mismo problema (sin tener nada más que Reaccionar) con Hooks :
OPCION 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;
}
OPCIÓN B: Alternativamente con useRef
que se comporta como una propiedad estática de una clase, lo que significa que no hace que el componente se vuelva a procesar cuando cambia su valor:
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;
}
Ejemplo: https://codesandbox.io/s/86n1wq2z8