react setStateメソッドを呼び出しても状態がすぐに変化しないのはなぜですか?


326

私はのフォームセクションを読んでいますドキュメントとonChange使用方法を示すためにこのコードを試してみました(JSBIN)。

var React= require('react');

var ControlledForm= React.createClass({
    getInitialState: function() {
        return {
            value: "initial value"
        };
    },

    handleChange: function(event) {
        console.log(this.state.value);
        this.setState({value: event.target.value});
        console.log(this.state.value);

    },

    render: function() {
        return (
            <input type="text" value={this.state.value} onChange={this.handleChange}/>
        );
    }
});

React.render(
    <ControlledForm/>,
  document.getElementById('mount')
);

<input/>ブラウザで値を更新するとconsole.loghandleChangeコールバック内の2番目valueが最初と同じように出力されますが、コールバックのスコープでconsole.log結果が表示さthis.setState({value: event.target.value})れないのはなぜhandleChangeですか?


回答:


615

Reactのドキュメントから:

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

状態が変化した後に関数を実行する場合は、コールバックとして渡します。

this.setState({value: event.target.value}, function () {
    console.log(this.state.value);
});

いい答えだ。私がする必要がある観察は、valueLinkを使用するように注意することです。入力をフォーマット/マスクする必要がない場合に有効です。
Dherik、2015

42
また、チェックアウトすることもできcomponentDidUpdateます。状態が変化した後に呼び出されます。
Keysox、

1
簡単な質問ですが、コールバックとして必要な関数をsetStateに渡したら、render()が呼び出される前にfuncが最初に実行されることを期待していました。しかし、順序はsetState()-> render()-> setStates 'callback()です。これは正常ですか?コールバックで行うことに基づいてレンダーを制御したい場合はどうでしょうか?shouldComponentUpdate
semuzaboi 2016年

2
shouldComponentUpdate特に指定されていない動作がない限り、コンポーネントの状態を変更すると、常に再レンダリングがトリガーされます。setState再レンダリングの前に発生させたい、渡そうとしているコールバックで正確に何をしようとしていますか?
マイケルパーカー、

4
...なぜ?誰かがこれを正当化できますか?
JackHasaKeyboard 2017

49

Reactのドキュメントで説明されているように、setState同期的に起動される保証はないため、console.log更新前の状態を返すことができます。

Michael Parkerは、内でコールバックを渡すことについて言及していますsetState。状態変更後のロジックを処理するもう1つの方法は、componentDidUpdateライフサイクルメソッドを使用する方法です。これは、Reactドキュメントで推奨されている方法です。

通常、そのようなロジックではなく、componentDidUpdate()を使用することをお勧めします。

これはsetState、s が連続して実行される可能性があり、状態が変化するたびに同じ関数を実行したい場合に特に便利です。各setStateにコールバックを追加するのではなく、componentDidUpdate必要に応じて内部に特定のロジックを使用して、内に関数を配置できます。

// example
componentDidUpdate(prevProps, prevState) {
  if (this.state.value > prevState.value) {
    this.foo();  
  }
}

16

ES7 async / awaitを使用してみてください。たとえばあなたの例を使用して:

handleChange: async function(event) {
    console.log(this.state.value);
    await this.setState({value: event.target.value});
    console.log(this.state.value);
}

あなたの答えは他の高品質の答えとどのように異なりますか?
tod

9
もう1つの答えは、setState()でのコールバックの使用に関するものです。これは、コールバックのユースケースが適用されない人のためにここに置いたと思いました。たとえば、私自身がこの問題に直面したとき、私のユースケースには、設定直後の更新された状態のスイッチケースが含まれていました。したがって、コールバックを使用するよりも、非同期/待機を使用することをお勧めします。
kurokiiru

ある状態を更新するときに常にawait alwaysを使用し、更新が完了するのを待つ場合、これはパフォーマンスに影響しますか?また、複数の待機しているsetStateを1つ下のチェーンに配置すると、各setStateの更新後にレンダリングされますか?または最後のsetState更新後?
user2774480


あなたがuser2774480に求めていることについては、どの実装を使用するかを決定するためのすべてが特定のユースケースに帰着すると思います。複数のsetStatesがチェーンで使用されている場合、パフォーマンスに影響があり、はい、各setStateの後でレンダリングされますが、間違っている場合は修正してください。
クロキイル


-1

async-await 構文は次のようなものに対して完全に機能します...

changeStateFunction = () => {
  // Some Worker..

  this.setState((prevState) => ({
  year: funcHandleYear(),
  month: funcHandleMonth()
}));

goNextMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}

goPrevMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}

-1

単に置く-this.setState({data:value})は本質的に非同期です。つまり、コールスタックから移動し、解決されない限り、コールスタックに戻るだけです。

イベントループを読んで、JSの非同期の性質と、更新に時間がかかる理由を明確に理解してください-

https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4

したがって-

    this.setState({data:value});
    console.log(this.state.data); // will give undefined or unupdated value

更新には時間がかかるので。上記のプロセスを達成するために-

    this.setState({data:value},function () {
     console.log(this.state.data);
    });
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.