web-dev-qa-db-ja.com

反応するsetStateコールバックには更新された状態がありません

if _monthOffset = 12_条件はtrueと評価され、yearOffset状態が2017 ifyearOffset = 2018_に更新されます。反応ドキュメントと私が読んだ他の回答に基づいて、_this.setState_のコールバック関数は、状態が更新された後に起動しますが、console.log()はまだ2018を出力しています。他の関連する質問からの回答に基づいてこのコードを実装するさまざまな方法がありますが、私の方法は機能しません。理由はわかりません。

_handleClick(e) {
  const { monthOffset, yearOffset } = this.state
  this.setState({ monthOffset: monthOffset - 1 })
  if ( monthOffset - 1 === 11 ) { this.setState((prevState) => { 
    return { yearOffset: prevState.yearOffset - 1 } },
    () => {console.log("yearOffset", yearOffset)}
  )}
  console.log("clicked")
}
_
6
Ryan Sam

おそらく、次の方法でロジックを単純化して、予期しない結果を引き起こす可能性のあるsetStateの複数の呼び出しを避けることができます。

handleClick(e) {

  const { monthOffset, yearOffset } = this.state

  // call setState once
  this.setState({ 

    // Always decrement month offset
    monthOffset : monthOffset - 1, 

    // Only decrement year offset if current month offset === 12
    yearOffset : (monthOffset === 12) ? yearOffset - 1 : yearOffset

  }, () => {

    console.log("state updated to", this.state)
  })

  console.log("clicked")
}
5
Dacre Denny

ReactでsetStateを呼び出すには、object setState"functional setState" の2つの一般的なパターンがあります。 機能的なsetStateは通常、現在の状態のときに使用されます (または「前の状態」、または古い状態を呼び出したいもの) setState呼び出しで呼び出されます。 これは、setStateが非同期であるために行われ、その結果、後続のsetStatesは、Reactが最初のsetStateサイクル。

setState呼び出しで既存の状態を使用したため、機能的なsetStateを使用する適切な場所になります。交換

this.setState({ monthOffset: monthOffset - 1 })

this.setState(monthOffset => {return {monthOffset: monthOffset - 1}})

私がこれを最初に見たときにあなたが私のような人なら、あなたは「ハァッ?それは私が持っているものとどう違うの?」と思うかもしれません。違いは、setStateにオブジェクトではなく関数が渡されると、通常の解決プロセスを実行するのではなく、更新をキューに入れることです。

または、あなたはこれを考えていないかもしれません。実際には、2回目のsetState呼び出しで関数setStateを使用しました。あなたの最初のものでもそれを使用すると、物事が正しくキューに入れられます。

2
Andy Taton

ドキュメントには、コールバックは常に機能すると書かれていますが、経験から、期待どおりの結果が常に返されるとは限りません。ステート自体の内部で可変オブジェクトを使用することと関係があると思います。

ドキュメント: https://reactjs.org/docs/react-component.html#setstate

コールバックに完全に依存することはできません。代わりに、作成できるのは

var stateObject = this.state

オブジェクトに必要な変更を加えます。

stateObject.monthOffset -= 1

そして、次のように状態を設定します。

this.setState(stateObject);

そうすれば、nextStateのコピーがstateObjectの中にあります

明確にするために:状態を設定する前にすべての評価を行いたいので、次のようにします:

monthOffset -= 1

次にif (monthOffset === 12) yearOffset -=1;

その後var stateObj = {monthOffset: monthOffset, yearOffset: yearOffset}

次にthis.setState(stateObj);


ドキュメントから: "setState()の2番目のパラメーターは、setStateが完了してコンポーネントが完了すると実行されるオプションのコールバック関数です。 一般に、そのようなロジックの代わりにcomponentDidUpdate()を使用することをお勧めします。 "

したがって、基本的に次の状態を取得する場合は、setState()を呼び出す関数内にそのコピーを保持するか、nextStateからcomponentDidUpdateを取得する必要があります。


後考:パラメーターとしてsetState()に渡すものはすべて、参照によって渡されます(値ではなく)。そのため、状態内にSearchFilters: {}オブジェクトがあり、setState()への呼び出し内にある場合、

setState({SearchFilters: DEFAULT_SEARCH_FILTERS}); // do not do this

「検索フィルター」と呼ばれるフォームを消去するためにSearchFiltersDEFAULT_SEARCH_FILTERSに設定したかもしれませんが、代わりにDEFAULT_SEARCH_FILTERS(定数)をSearchFiltersに効果的に設定して、DEFAULT_SEARCH_FILTERSを消去します。 。

予想される行動?教えてください。

2
ihodonald