Syncではなくreactjs AsyncでsetStateを使用するのはなぜですか?


126

私はちょうどthis.setState()任意のコンポーネントの反応関数が非同期であるか、それが呼び出された関数の完了後に呼び出されることを発見しました。

今、私はこのブログを検索して見つけました(setState()State Mutation Operation May Resyncous In ReactJS

ここで彼はそれを発見しました setStateは、状態の変化がどのようにトリガーされたかに応じて、非同期(スタックが空のときに呼び出される)または同期(呼び出されるとすぐに呼び出される)。

今これら二つは消化するのが難しい

  1. ブログでは、setState関数は関数内で呼び出されますupdateStateが、updateState関数をトリガーしたのは、呼び出された関数が知っているものではありません。
  2. setStateJSが単一スレッド言語であり、このsetStateがWebAPIまたはサーバー呼び出しではないため、なぜJSのスレッドでのみ実行する必要があるのか、なぜ非同期になるのでしょうか。彼らはこれを行っているので、再レンダリングによってすべてのイベントリスナーなどが停止することはありませんか、それとも他の設計上の問題があります。


1
私の周りの気候のビットを記述することができます今日の記事を書いたsetStatemedium.com/@agm1984/...を
agm1984

回答:


155

状態値が更新された後で関数を呼び出すことができます。

this.setState({foo: 'bar'}, () => { 
    // Do something here. 
});

また、一度に更新する状態がたくさんある場合は、それらをすべて同じグループにまとめます setState

の代わりに:

this.setState({foo: "one"}, () => {
    this.setState({bar: "two"});
});

これを行うだけです:

this.setState({
    foo: "one",
    bar: "two"
});

16
はい、大丈夫です。使用できるcallBack関数を取得しましたが、問題はありません。
Anup

12
うまくいけば、それが他の誰かがこの質問をつまずくのを助けるでしょう。
JoeTidee

2
ya datが役立つ
Anup

97

1)setStateアクションは非同期であり、パフォーマンス向上のためにバッチ処理されます。これはのドキュメントで説明されていますsetState

setState()はthis.stateをすぐには変更しませんが、保留中の状態遷移を作成します。このメソッドを呼び出した後にthis.stateにアクセスすると、既存の値が返される可能性があります。setStateの呼び出しの同期操作の保証はなく、パフォーマンスを向上させるために呼び出しをバッチ処理することができます。


2)JSはシングルスレッド言語であり、これsetStateはWebAPIまたはサーバーコールではないため、setStateを非同期にするのはなぜですか?

これはsetState、状態を変更し、再レンダリングを引き起こすためです。これは負荷の高い操作になる可能性があり、同期させるとブラウザが応答しなくなる可能性があります。

したがって、setState呼び出しは非同期であるだけでなく、UIエクスペリエンスとパフォーマンスを向上させるためにバッチ処理されます。


59
setState呼び出しが行われた後にイベントの順序を確認する必要がある場合は、コールバック関数を渡すことができます。 this.setState({ something: true }, () => console.log(this.state))
ianks 2016年

1
説明をありがとう@Sachin。しかし、私はまだ疑っています、ブログが説明するように同期することはできますか?
Ajay Gaur 2016

2
もう一つの愚かな設計決定が反応しています。状態の更新を同期し、レンダリングを非同期にします。レンダリングをバッチ処理できますが、競合状態を処理する必要なく、何かを状態変数としてプリミティブとして設定できるようにしたいと考えています。
ig-dev

機能を非同期または同期にするオプションの設定を許可しないのはなぜですか?これは便利な機能です
Randall Coding

明らかに、私は反応チームではありませんが、私の意見では、状態更新を非同期にする1つの理由は、ブラウザーがシングルスレッドであることです。同期操作により、UIが応答しなくなる可能性があり、UIの候補としては適していません。
Sachin

16

私はこの質問が古いことを知っていますが、私を含め、多くのreactjsユーザーに長い間多くの混乱を引き起こしています。最近、(reactチームの)Dan Abramovが、なぜの性質setStateが非同期であるかについての素晴らしい説明を書きました:

https://github.com/facebook/react/issues/11527#issuecomment-360199710

setState非同期であることを意図しており、Dan Abramovによるリンクされた説明には、そのためのいくつかの本当に良い理由があります。これは、常に非同期であるという意味ではありません。主に、同期であることに依存できないことを意味します。ReactJSは、状態を変更するシナリオの多くの変数を考慮して、実際にを更新してコンポーネントを再レンダリングする時期を決定します。 これを示す簡単な例は、ユーザーアクションへの反応として呼び出すと、おそらくすぐに更新されます(ただし、これを当てにすることはできません)。したがって、ユーザーは遅延を感じません。 、しかしあなたが電話した場合state
setStatestatesetState ajax呼び出し応答またはユーザーによってトリガーされないその他のイベントに反応して、ユーザーはこの遅延を実際には感じないため、状態がわずかに遅延して更新される可能性があります。複数の状態更新をまとめてバッチ処理し、DOMを再レンダリングする回数を減らします。


答えを正しいものとしてマークしていません。人々はそれを回避する方法を投稿しています。尋ねられた質問に対する答えではありません。この記事は良さそうです。
Anup

@Anup答えは、単に「非同期」または「同期」よりも少し複雑です。常に「非同期」として扱う必要がありますが、場合によっては「同期」することがあります。私はあなたにいくつかの光を当てるといいのですが。
gillyb 2018年

8

こちらの良い記事 https://github.com/vasanthk/react-bits/blob/master/patterns/27.passing-function-to-setState.md

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

Solution
this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

またはコールバックを渡します this.setState ({.....},callback)

https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82 https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b



1

いくつかのコンポーネントでカウンターをインクリメントすることを想像してください:

  class SomeComponent extends Component{

    state = {
      updatedByDiv: '',
      updatedByBtn: '',
      counter: 0
    }

    divCountHandler = () => {
      this.setState({
        updatedByDiv: 'Div',
        counter: this.state.counter + 1
      });
      console.log('divCountHandler executed');
    }

    btnCountHandler = () => {
      this.setState({
        updatedByBtn: 'Button',
        counter: this.state.counter + 1
      });
      console.log('btnCountHandler executed');
    }
    ...
    ...
    render(){
      return (
        ...
        // a parent div
        <div onClick={this.divCountHandler}>
          // a child button
          <button onClick={this.btnCountHandler}>Increment Count</button>
        </div>
        ...
      )
    }
  }

親コンポーネントと子コンポーネントの両方にアタッチされたカウントハンドラがあります。これは意図的に行われるため、同じクリックイベントのバブリングコンテキスト内で、2つの異なるハンドラー内からsetState()を2回実行できます。

想像できるように、バブリングフェーズ中にイベントがターゲットから最も外側のコンテナにバブルするため、ボタンの1回のクリックイベントで両方のハンドラーがトリガーされます。

したがって、btnCountHandler()が最初に実行され、カウントが1にインクリメントされることが予想され、次にdivCountHandler()が実行され、カウントが2にインクリメントされることが予想されます。

ただし、React Developerツールで検査できるため、カウントは1までしか増加しません。

これは反応することを証明します

  • すべてのsetState呼び出しをキューに入れる

  • コンテキストの最後のメソッド(この場合はdivCountHandler)を実行した後、このキューに戻ります

  • 同じコンテキスト内の複数のsetState呼び出し内で発生するすべてのオブジェクト変更をマージします(たとえば、単一のイベントフェーズ内のすべてのメソッド呼び出しは同じコンテキストです)、単一のオブジェクト変更構文にマージします(これは、状態プロパティを個別に更新できる理由ですそもそもsetState()で)

  • そして、それを1つのsetState()に渡して、複数のsetState()呼び出しによる再レンダリングを防止します(これはバッチ処理の非常に原始的な説明です)。

反応によって実行される結果のコード:

this.setState({
  updatedByDiv: 'Div',
  updatedByBtn: 'Button',
  counter: this.state.counter + 1
})

この動作を停止するには、オブジェクトを引数としてsetStateメソッドに渡す代わりに、コールバックが渡されます。

    divCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByDiv: 'Div',
              counter: prevState.counter + 1
            };
          });
          console.log('divCountHandler executed');
        }

    btnCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByBtn: 'Button',
              counter: prevState.counter + 1
            };
          });
      console.log('btnCountHandler executed');
    }

最後のメソッドが実行を終了し、reactがsetStateキューを処理するために戻ると、キューに入れられた各setStateのコールバックを呼び出し、前のコンポーネントの状態を渡します。

このように反応することで、キュー内の最後のコールバックが、以前のすべての対応者が行った状態を更新できるようになります。


0

はい、setState()は非同期です。

リンクから:https : //reactjs.org/docs/react-component.html#setstate

  • Reactは、状態の変更がすぐに適用されることを保証しません。
  • setState()は常にコンポーネントをすぐに更新するわけではありません。
  • setState()は、コンポーネントを更新するための即時コマンドではなく、要求と考えてください。

彼らが考えるので
リンクから:https : //github.com/facebook/react/issues/11527#issuecomment-360199710

... setState()を同期的に再レン​​ダリングすると多くの場合非効率になることに同意します

非同期のsetState()を使用すると、初心者や経験の浅い人でも生活が非常に困難になります。-
予期しないレンダリングの問題:レンダリングの遅延またはレンダリングなし(プログラムロジックに基づく)
-パラメータの受け渡しは、
他の問題の中でも特に重要です。

以下の例が役立ちました:

// call doMyTask1 - here we set state
// then after state is updated...
//     call to doMyTask2 to proceed further in program

constructor(props) {
    // ..

    // This binding is necessary to make `this` work in the callback
    this.doMyTask1 = this.doMyTask1.bind(this);
    this.doMyTask2 = this.doMyTask2.bind(this);
}

function doMyTask1(myparam1) {
    // ..

    this.setState(
        {
            mystate1: 'myvalue1',
            mystate2: 'myvalue2'
            // ...
        },    
        () => {
            this.doMyTask2(myparam1); 
        }
    );
}

function doMyTask2(myparam2) {
    // ..
}

お役に立てば幸いです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.