Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. 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. 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")
}
like image 264
Ryan Sam Avatar asked Aug 22 '18 01:08

Ryan Sam


People also ask

Why React setState setState does not update immediately?

State updates in React are asynchronous; when an update is requested, there is no guarantee that the updates will be made immediately. The updater functions enqueue changes to the component state, but React may delay the changes, updating several components in a single pass.

Why is my React state not updating?

The reason why the state doesn't update immediately is because for each render, the state is immutable. …are both constants values ! So they're immutable. The state remains constant inside the render but can be changed between two renders.

Does setState accept callback?

The setState function takes an optional callback parameter that can be used to make updates after the state is changed. This function will get called once the state has been updated, and the callback will receive the updated value of the state.

How do I change state in React with setState?

The setState() Method State can be updated in response to event handlers, server responses, or prop changes. This is done using the setState() method. The setState() method enqueues all of the updates made to the component state and instructs React to re-render the component and its children with the updated state.


1 Answers

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

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

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;

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

then 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."

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


Afterthoughts: Anything that you pass into setState() as a parameter is passed by reference (not by value). So, if you have an object SearchFilters: {} within your state, and inside your call to setState(), you have

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.

Expected behavior? You tell me.

like image 126
ihodonald Avatar answered Sep 18 '22 00:09

ihodonald