1. ホーム
  2. reactjs

[解決済み] componentWillUnmountで取得をキャンセルする方法

2022-09-12 21:21:44

質問

タイトルにある通りだと思います。取得中のコンポーネントをアンマウントするたびに、黄色い警告が表示されます。

コンソール

警告 を呼び出すことができません。 setState (または forceUpdate ) をアンマウントされたコンポーネントの上に置くことができます。これは仕方がないことですが、、、。修正するには、サブスクリプションと非同期タスクをすべてキャンセルして componentWillUnmount メソッドですべてのサブスクリプションと非同期タスクをキャンセルします。

  constructor(props){
    super(props);
    this.state = {
      isLoading: true,
      dataSource: [{
        name: 'loading...',
        id: 'loading',
      }]
    }
  }

  componentDidMount(){
    return fetch('LINK HERE')
      .then((response) => response.json())
      .then((responseJson) => {
        this.setState({
          isLoading: false,
          dataSource: responseJson,
        }, function(){
        });
      })
      .catch((error) =>{
        console.error(error);
      });
  }

どのように解決するのですか?

Promise を実行すると、それが解決されるまでに数秒かかることがあり、その間にユーザーはアプリ内の別の場所に移動しているかもしれません。そのため、Promiseが解決されるときに setState はマウントされていないコンポーネントで実行され、エラーが発生します。また、メモリリークも発生する可能性があります。

そういうわけで、非同期ロジックの一部をコンポーネントの外に出すのがベストなのです。

そうでない場合は、何らかの方法で プロミスをキャンセルする . あるいは、最後の手段として(アンチパターンですが)、コンポーネントがまだマウントされているかどうかをチェックするための変数を保持することができます。

componentDidMount(){
  this.mounted = true;

  this.props.fetchData().then((response) => {
    if(this.mounted) {
      this.setState({ data: response })
    }
  })
}

componentWillUnmount(){
  this.mounted = false;
}

もう一度強調しておきますが、この はアンチパターンです。 で十分なのですが、あなたの場合、この Formik の実装と同じです)。

に関する同様の議論は GitHub

EDITです。

これはおそらく、同じ問題(React以外何も持っていない)を解決するために フック :

OPTION 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; // track whether component is mounted

    request.get(url)
      .then(result => {
        if (isMounted) {
          setState(result);
        }
      });

    return () => {
      // clean up
      isMounted = false;
    };
  }, []); // only on "didMount"

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

// or extract it to custom hook:
function useIsMounted() {
  const isMounted = React.useRef(true)

  useEffect(() => {
    return () => {
      isMounted.current = false;
    };
  }, []);

  return isMounted; // returning "isMounted.current" wouldn't work because we would return unmutable primitive
}

https://codesandbox.io/s/86n1wq2z8