反应 setState 回调没有更新状态

react setState callback doesn't have the updated state

if monthOffset = 12 如果 yearOffset = 2018,条件将评估为 true 并将 yearOffset 状态更新为 2017。根据我读过的反应文档和其他答案,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")
}

也许您可以通过以下方式简化您的逻辑,以避免多次调用 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")
}

文档说回调总是有效,但我从经验中知道它并不总是 return 您所期望的。我认为这与在状态本身内部使用可变对象有关。

文档:https://reactjs.org/docs/react-component.html#setstate

不能完全依赖回调。相反,您可以做的是创建

var stateObject = this.state

对对象进行任何必要的更改:

stateObject.monthOffset -= 1

然后像这样设置状态:

this.setState(stateObject);

这样你就可以在 stateObject

中获得 nextState 的副本

澄清一下:您想在设置状态之前进行所有评估,所以这样做:

monthOffset -= 1

然后 if (monthOffset === 12) yearOffset -=1;

然后 var stateObj = {monthOffset: monthOffset, yearOffset: yearOffset}

然后 this.setState(stateObj);


From the documentation: "The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead."

所以基本上,如果您想获得下一个状态,您应该在调用 setState() 的函数中拥有它的副本,或者您应该从 componentDidUpdate 获得 nextState


事后思考: 您作为参数传递给 setState() 的任何内容都是 通过引用传递的 (而不是通过值)。所以,如果你在你的状态中有一个对象 SearchFilters: {},在你对 setState() 的调用中,你有

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

您可能已将 SearchFilters 设置为 DEFAULT_SEARCH_FILTERS 以清除名为 "Search Filters" 的表格,但实际上您实际上设置了 DEFAULT_SEARCH_FILTERS(一个常量) 到 SearchFilters,清除你的 DEFAULT_SEARCH_FILTERS.

Expected behavior? You tell me.

在 React 中调用 setState 有两种流行的模式:对象 setState"functional setState". (or "previous state", or whatever you want to call the old state) 这样做是因为 setState 是异步的,因此后续setStates 有时可以 运行 在 React 设法完成第一个 setState 周期之前。

您在 setState 调用中使用了现有状态,因此它是使用函数式 setState 的合适位置。替换

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

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

如果你和我一样,当我第一次看到这个时,你可能会想,"Huh? How is that any different than what I have?" 不同之处在于,当 setState 被传递一个函数而不是一个对象时,它会将更新排队通过通常的解决过程,确保事情按顺序完成。

或者您可能没有想到这一点;您实际上在第二个 setState 调用中使用了功能 setState 。在你的第一个中使用它也将确保事情正确排队。