reactjs - 渡し方 - 非同期componentDidMount()を使用して良いですか?




react 子から親 state (5)

React Nativeで非同期関数として componentDidMount() を使用していますか、それを避けるべきですか?

コンポーネントのマウント時に AsyncStorage から情報を取得する必要がありますが、それを可能にする唯一の方法は、 componentDidMount() 関数を非同期にすることです。

async componentDidMount() {
    let auth = await this.getAuth();
    if (auth) 
        this.checkAuth(auth);
}

それに問題はありますか、この問題に対する他の解決策はありますか?


あなたが何をしているのかを知っている限り、それは問題ないと思います。 ただし、 componentWillUnmount が実行され、コンポーネントがアンマウントされた後でも、 async componentDidMount() が実行されている可能性があるため、混乱する可能性があります。

componentDidMount 内で同期タスクと非同期タスクの両方を開始することもでき componentDidMountcomponentDidMount が非同期の場合、最初の await 前にすべての同期コードを配置する必要があります。 最初の await 前のコードが同期的に実行されることは、誰かには明らかではないかもしれません。 この場合、おそらく componentDidMount 同期を維持し componentDidMount が、syncメソッドとasyncメソッドを呼び出します。

async componentDidMount()async メソッドを呼び出すsync componentDidMount() どちらを選択する場合でも、コンポーネントのアンマウント時にまだ実行されている可能性のあるリスナーまたは非同期メソッドをクリーンアップする必要があります。


あなたのコードは素晴らしいし、私にとって非常に読みやすいです。 この Dale Jeffersonの記事を 参照してください。彼は非同期 componentDidMount 例を示しており、見た目もとても良いです。

しかし、コードを読んでいる人は、Reactが返された約束に対して何かをすることを想定していると言う人もいます。

したがって、このコードの解釈と、それが良い習慣であるかどうかは非常に個人的なものです。

別のソリューションが必要な場合は、 promises 使用できます。 例えば:

componentDidMount() {
    fetch(this.getAuth())
      .then(auth => {
          if (auth) this.checkAuth(auth)
      })
}

更新:

(私のビルド:React 16、Webpack 4、Babel 7):

Babel 7を使用すると、次のことがわかります。

このパターンを使用しています...

async componentDidMount() {
    try {
        const res = await fetch(config.discover.url);
        const data = await res.json();
        console.log(data);
    } catch(e) {
        console.error(e);
    }
}

次のエラーが発生します...

キャッチされないReferenceError:regeneratorRuntimeが定義されていません

この場合、 babel-plugin-transform-runtime をインストールする必要があり ます

https://babeljs.io/docs/en/babel-plugin-transform-runtime.html

何らかの理由で上記のパッケージ(babel-plugin-transform-runtime)をインストールしたくない場合は、Promiseパターンに固執したいでしょう...

componentDidMount() {
    fetch(config.discover.url)
    .then(res => res.json())
    .then(data => {
        console.log(data);
    })
    .catch(err => console.error(err));
}

私はいくつかの研究を行いましたが、1つの重要な違いを発見しました: Reactは非同期ライフサイクルメソッドからのエラーを処理しません。

したがって、次のように記述した場合:

componentDidMount()
{
    throw new Error('I crashed!');
}

エラーは エラー境界 でキャッチされ、 エラー を処理して適切なメッセージを表示できます。

このようにコードを変更した場合:

async componentDidMount()
{
    throw new Error('I crashed!');
}

これはこれと同等です:

componentDidMount()
{
    return Promise.reject(new Error('I crashed!'));
}

それから あなたのエラーは静かに飲み込まれ ます。 恥を知れ、React ...

それでは、どのようにエラーを処理しますか? 唯一の方法は、このような明示的なキャッチのようです:

componentDidMount()
{
    try
    {
         await myAsyncFunction();
    }
    catch(error)
    {
        //...
    }
}

またはこのように:

componentDidMount()
{
    myAsyncFunction()
    .catch(()=>
    {
        //...
    });
}

それでもエラー境界が豊富なエラーが必要な場合は、次のトリックを考えることができます。

  1. エラーをキャッチし、エラーハンドラーにコンポーネントの状態を変更させます
  2. 状態がエラーを示す場合、 render メソッドからスローし render

例:

class BuggyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: null };
  }

  buggyAsyncfunction(){ return Promise.reject(new Error('I crashed async!'));}

  async componentDidMount() {
    try
    {
      await this.buggyAsyncfunction();
    }
    catch(error)
    {
        this.setState({error: error});
    }
  }

  render() {
    if(this.state.error)
        throw this.state.error;

    return <h1>I am OK</h1>;
  }
}

async キーワードなしで componentDidMount を使用すると、ドキュメントは次のように言います。

componentDidMount()ですぐにsetState()を呼び出すことができます。 追加のレンダリングがトリガーされますが、ブラウザーが画面を更新する前に発生します。

async componentDidMount を使用すると、この機能が async componentDidMount ます。ブラウザーが画面を更新した後に別のレンダリングが発生します。 しかし、imo、データの取得など、非同期の使用を考えている場合、ブラウザが画面を2回更新することを避けることはできません。 別の世界では、ブラウザが画面を更新する前にcomponentDidMountを一時停止することはできません





react-native