【问题标题】:react setState callback doesn't have the updated statereact setState 回调没有更新状态
【发布时间】:2019-01-28 05:25:15
【问题描述】:

如果monthOffset = 12,条件将评估为真,如果yearOffset = 2018,则将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 回调中使用 console.log("yearOffset", yearOffset) 而不是匿名函数?

标签: javascript reactjs setstate


【解决方案1】:

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

文档: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);


来自文档: "setState() 的第二个参数是一个可选的回调函数,一旦 setState 完成并重新渲染组件就会执行。一般我们建议使用componentDidUpdate() 代替这种逻辑。"

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


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

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

您可能已将SearchFilters 设置为DEFAULT_SEARCH_FILTERS 以清除名为“搜索过滤器”的表单,但您实际上将DEFAULT_SEARCH_FILTERS(常量)设置为SearchFilters,清除了您的DEFAULT_SEARCH_FILTERS.

预期的行为?你告诉我。

【讨论】:

    【解决方案2】:

    在 React 中调用 setState 有两种流行的模式:对象 setState"functional setState"Functional setState is generally used when the current state(或“先前状态”,或任何你想调用的旧状态)is invoked in the setState call. 这是因为 setState 是异步的,因此后续 setStates 有时可以在 React 成功完成之前运行第一个setState 循环。

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

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

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

    如果你和我一样,当我第一次看到这个时,你可能会想,“嗯?这和我有什么不同?”不同之处在于,当 setState 被传递一个函数而不是一个对象时,它会将更新排队而不是通过通常的解析过程,从而确保事情按顺序完成。

    或者你可能不这么想;您实际上在第二次 setState 通话中使用了功能性 setState。在你的第一个中使用它也将确保事情正确排队。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 这是一个更有效的代码变体,我得到了我想要的结果。我认为问题在于当我调用console.log("yearOffset", yearOffset) 时,它正在读取来自const 析构函数的值,我认为在调用setState 后它没有更新。也许为了使我的变体起作用,我必须在回调中创建一个新的析构函数。
      • 请注意,除了将信息记录到控制台之外,您不应出于任何原因使用回调来获取信息。回调并不总是有效。您还可以使用解构器从状态中提取变量以获得更有效的解决方案,但根据我的经验,我从未注意到复制整个状态本身有任何延迟。
      • @ihodonald 但是文档不是说它总是在状态更新后调用吗?我认为文档是正确的,我只是从析构函数中读取错误的值。正如我上面所说,我需要在回调函数中使用this.state 的新析构函数来获取新值。我试过了,它确实有效。然而,这个答案是使用条件和回调的更好方法,所以我最终使用了它。
      • 回调在大多数情况下确实有效,但对于需要在下一个状态执行的任何其他类型的逻辑,它不应该完全依赖。谨慎使用它。您可以将它用于控制台的简单日志,但我不相信它每次都返回正确的值。文档还说您应该使用 componentDidUpdate 来获取 nextState。您总是可以将状态中想要的内容解构为变量(或常量),更改该变量,然后将 nextState 放入该变量中。
      • @RyanSam 这不能回答你的问题,但它可能会给你一些见解:reactjs.org/docs/reconciliation.html#the-diffing-algorithm
      猜你喜欢
      • 1970-01-01
      • 2020-03-03
      • 1970-01-01
      • 2017-09-05
      • 2018-08-11
      • 2022-12-09
      • 1970-01-01
      • 2020-05-09
      相关资源
      最近更新 更多