Promiseを起動すると、解決するまでに数秒かかる場合があり、その時点でユーザーはアプリ内の別の場所に移動している可能性があります。したがって、Promiseの解決setState
がマウントされていないコンポーネントで実行されると、エラーが発生します-あなたの場合と同じです。これにより、メモリリークが発生する可能性もあります。
そのため、非同期ロジックの一部をコンポーネントから移動するのが最善です。
それ以外の場合は、何らかの方法でPromiseをキャンセルする必要があります。または、最後の手段として(アンチパターンです)、変数を保持して、コンポーネントがまだマウントされているかどうかを確認できます。
componentDidMount(){
this.mounted = true;
this.props.fetchData().then((response) => {
if(this.mounted) {
this.setState({ data: response })
}
})
}
componentWillUnmount(){
this.mounted = false;
}
もう一度強調しておきますが、これはアンチパターンですが、あなたの場合は十分かもしれません(Formik
実装の場合と同じように)。
GitHubに関する同様の議論
編集:
これはおそらく、フックで同じ問題(Reactしか持たない)を解決する方法です:
オプション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;
}
オプションB:あるいはuseRef
、クラスの静的プロパティのように動作します。つまり、値が変更されたときにコンポーネントが再レンダリングされません。
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;
}
例:https://codesandbox.io/s/86n1wq2z8