简体   繁体   English

react setState 回调没有更新状态

[英]react setState callback doesn't have the updated state

if monthOffset = 12 the condition will evaluate to true and update the yearOffset state to 2017 if yearOffset = 2018 .如果monthOffset = 12条件将评估为真,如果yearOffset = 2018则将yearOffset状态更新为 2017。 Based on the react docs and other answers I've read, the callback function in this.setState fires after the state has been updated, yet the console.log() is still outputting 2018. I've tried a couple different methods of implementing this code based on answers form other related questions but mine isn't working.根据我阅读的反应文档和其他答案, this.setState中的回调函数在状态更新后触发,但console.log()仍在输出 2018。我尝试了几种不同的实现方法此代码基于其他相关问题的答案,但我的不起作用。 I'm not sure why.我不确定为什么。

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")
}

Perhaps you could simplify your logic in the following way, to avoid multiple calls to setState which may be causing unexpected results:也许您可以通过以下方式简化逻辑,以避免多次调用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")
}

The documentation says that the callback always works, but I know from experience that it doesn't always return what you're expecting.文档说回调总是有效的,但我从经验中知道它并不总是返回你所期望的。 I think it has something to do with using mutable objects inside the state itself.我认为这与在状态本身内部使用可变对象有关。

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

You can't completely rely on the callback.你不能完全依赖回调。 Instead, what you can do is create相反,您可以做的是创建

var stateObject = this.state

Make any necessary changes to the object:对对象进行任何必要的更改:

stateObject.monthOffset -= 1

and then set the state like this:然后像这样设置状态:

this.setState(stateObject);

That way you have a copy of the nextState inside stateObject这样你就有了nextState内的stateObject的副本

To clarify: You want to do all of the evaluation before you set the state, so do:澄清:您想在设置状态之前进行所有评估,因此请执行以下操作:

monthOffset -= 1

then if (monthOffset === 12) yearOffset -=1;那么if (monthOffset === 12) yearOffset -=1;

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

then this.setState(stateObj);然后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()的第二个参数是一个可选的回调函数,一旦 setState 完成并重新渲染组件就会执行。通常我们建议使用 componentDidUpdate() 来代替这种逻辑。

So basically if you want to get the next state, you should either have a copy of it inside your function that calls setState() or you should get the nextState from componentDidUpdate所以基本上如果你想获得下一个状态,你应该在调用setState()的函数中拥有它的副本,或者你应该从componentDidUpdate获取nextState


Afterthoughts: Anything that you pass into setState() as a parameter is passed by reference (not by value).事后思考:作为参数传递给setState()的任何内容都是通过引用(而不是通过值)传递的。 So, if you have an object SearchFilters: {} within your state, and inside your call to setState() , you have因此,如果您的状态中有一个对象SearchFilters: {} ,并且在您对setState()的调用中,您有

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

You may have set SearchFilters to DEFAULT_SEARCH_FILTERS in an effort to clear out a form called "Search Filters", but instead you will have effectively set DEFAULT_SEARCH_FILTERS (a constant) to SearchFilters , clearing out your DEFAULT_SEARCH_FILTERS .您可能已将SearchFilters设置为DEFAULT_SEARCH_FILTERS以清除名为“搜索过滤器”的表单,但您将有效地将DEFAULT_SEARCH_FILTERS (常量)设置为SearchFilters ,清除您的DEFAULT_SEARCH_FILTERS

Expected behavior?预期的行为? You tell me.你告诉我。

There are two prevalent patterns to calling setState in React: object setState , and "functional setState " .在 React 中调用setState有两种流行的模式:对象setState“功能setState Functional setState is generally used when the current state (or "previous state", or whatever you want to call the old state) is invoked in the setState call.函数式 setState 通常setState调用中调用当前状态(或“先前状态”,或任何您想调用的旧状态)时使用。 This is done because setState is asynchronous, and as a result subsequent setStates can sometimes run before React has managed to complete the first setState cycle.这样做是因为setState是异步的,因此后续的setStates有时可以在 React 成功完成第一个setState循环之前运行。

You have used existing state in your setState call, so it would be an appropriate place to use functional setState .您在setState调用中使用了现有状态,因此它是使用功能性setState的合适位置。 Replace代替

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

with

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

If you are like me when I first saw this, you might be thinking, "Huh? How is that any different than what I have?"如果你和我一样,当我第一次看到这个时,你可能会想,“嗯?这和我有什么不同?” The difference is that when setState is passed a function instead of an object, it queues the update instead of going through its usual resolution process, which ensures things get done in order.不同之处在于,当setState被传递给函数而不是对象时,它会将更新排队而不是通过通常的解析过程,从而确保事情按顺序完成。

Or you might not be thinking this;或者你可能不这么想; you have actually used functional setState in your second setState call.您实际上在第二次setState调用中使用了功能性setState Using it in your first one too will make sure things are queued correctly.在你的第一个中使用它也将确保事情正确排队。

This happens because you are using the yearOffset that you've read on top, from the previous state (before your this.setState has run).发生这种情况是因为您使用的是从前一个状态(在this.setState yearOffset
You should instead read the state from the callback directly, like this:您应该直接从回调中读取状态,如下所示:

() => console.log("yearOffset", this.state.yearOffset)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM