-
Notifications
You must be signed in to change notification settings - Fork 47k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Not clear how setState in this case works #9439
Comments
Assuming React is enqueuing the state changes and executing them, they will be executed in order. 1st example output - 2nd example output - |
@hanumanthan So if I want to ensure I always keep track of the latest value in state, each Also maybe it is time react puts a doc explaining all the pitfalls one can encounter with managing state. |
Why do you need to “keep track of the latest value in state”? It would be easier to answer if you explained the use case in more detail. Right now it’s confusing why you call |
In other words: this sounds like an abstract question that is hard to answer without confusing you further. It would be much easier to answer if you provided a snippet of real product code where you’re trying to solve a specific problem. |
The simplest answer to your question is: if you want to keep track of the latest value in the state, you can log it in componentDidUpdate() {
console.log('state is', this.state);
} |
@gaearon Thanks I will think about it. Btw. asynchronicity of setState I have seen has confused many developers, was making setState async worth it? maybe it is better if it is synchronous? For example this:http://stackoverflow.com/questions/43428456/how-to-correctly-handle-state-in-this-case, I would be grateful if you can respond on that question. |
Yes, it’s totally worth it, especially in bigger apps. We should improve the API to make it less confusing in the future, but definitely not go back to synchronous updates.
I looked at your question but the way you schedule a timeout inside the updater function is confusing to me. Updater functions should be “pure”, that is, they should only describe how the state changes. You’re not supposed to trigger any side effects in them (like setting timeouts), or mutate parts of the state (like you do with The way you keep track of an async operation with It would be easier to answer this question if you created a small example (e.g. on CodePen or JSBin) and explained what exactly the code is supposed to do. It is likely there’s a simpler way to satisfy the requirements, and model the state in a different way. But it’s hard to help when the code example is too abstract, and the line between the original requirements and the implementation is not clear. |
Ah, I see, yes. Sorry I missed that. I guess I’m confused by the overall flow of the code. It would really help to know how you want it to work, and I could then write a more React-y version. |
@gaearon Dan, how can many cells set inProcess flag, in the beginning of function you can see I am checking it and quiting the function if it is set. |
^^ see my reply above 😉 |
@gaearon So now you think this is OKAY? PS. this is code from my project: https://github.com/giorgim/MatchingGame. the timeout there I need for UI effect. |
I think the confusing part here is that you’re both setting the state and want to set a timeout based on that state (and in the timeout, set the state again). I think that if you split the timeout-related part of this logic into the It might be worth encoding these possible game states more directly into your component state and think more carefully about how to represent them with objects. For example, it might be that instead of an array of cell values it is easier to think about an explicit state like: {
openedCells: [1, 2], // array of ids
firstSelectedCell: 5, // could be null
secondSelectedCell: 7, // could be null
} and then implement conditional logic in handleClick(e) {
// Are we waiting for a timeout? Reset it.
if (this.resetTimeout) {
clearTimeout(this.resetTimeout);
}
const id = ... // get it from target node, or bind event handler to ID in render()
this.setState(prevState => {
if (prevState.firstSelectedCell !== null && prevState.secondSelectedCell === null) {
// There is just one selected cell. We clicked on the second one.
return {
secondSelectedCell: id
};
}
// We are selecting the first cell
// (either because we clicked to reset both or because none were selected).
return {
firstSelectedCell: id,
secondSelectedCell: null
};
}
componentDidUpdate(prevState) {
if (prevState.secondSelectedCell !== this.state.secondSelectedCell) {
// We just picked the second cell.
if (isSamePicture(
this.state.secondSelectedCell,
this.state.firstSelectedCell
) {
// Same picture! Keep them open.
this.setState(prevState => {
// Add them both to opened cells and reset.
return {
firstSelectedCell: null,
secondSelectedCell: null,
openedCells: [
...prevState.openedCells,
prevState.firstSelectedCell,
prevState.secondSelectedCell
]
};
} else {
// Clear both in a second.
this.resetTimeout = setTimeout(() => {
this.setState({
firstSelectedCell: null,
secondSelectedCell: null,
});
}, 1000);
}
} Then, in the I omitted the details but I hope this gives you something to get started with, and hopefully provides a view into a more idiomatic React approach to this. |
@gaearon Thanks I will think about your suggestion. I think since my code works (it doesn't have any obvious bugs, does it?) at least I may leave as it is and incorporate your suggestion maybe in future projects. |
Yea sure the original code looks okay (before you started using the updater form of The way you were using the updater form is wrong (as I said, you shouldn't trigger side effects from it, and also shouldn't reference parts of the state and later mutate them independently). So I'd just suggest sticking to the simpler In today's version of React the difference only matters if |
@gaearon "before you started using the updater form of setState" what do you mean here Dan? I got confused in your last message at which times to which part of my code you refer to :) PS.I think it is time Facebook puts out official doc named smth like "state management in react" where it explains all the pitfalls and best practices about state management, given so much confusion about it on the net |
I meant that the code in your GitHub project that just calls Using the function version of PS Everything we have to say on this topic is already in the docs. Please check "State and Lifecycle" 😉. The |
@gaearon If you look at my SO question, there are two versions, so you say 1st makes more sense? But in that case can't I theoretically run into case that where I clone the state (third or fourth statement) in clickHandler, I can get old state? (because some previous setState-s were pending?) |
Not in the current version of React, no. If we're inside an event handler then |
-- where could be that "after that"? PS. So you endorse first version of code which was included in my SO question, if you give an answer on Stack Overflow about that I will accept that - it may also help others. |
@gaearon Dan how you explain that here: http://stackoverflow.com/a/42994068/3963067, in the first code snippet (click first "show code snippet") by the author, inside Because this seems to contradict with what you put on SO answer
|
In the code snippet, there are 2 event handlers called simultaneously, this.state in 1st event handler gets the correct value whereas this.state in 2nd event handler is read after setState have been called. This is inline with the above answer as it says this.state before the first setState will have correct value @giorgim |
@hanumanthan , @gaearon Not really. Citing
The second time the event handler is called, we are entering a brand new event handler, so what Dan said above should hold. Of course after I read state in event handler at some point I will modify it, and if when event handler is called again, what dan said above doesn't hold, then the question and answer also doesn't make much sense. because in that case it appears that is is safe to read from event handler only once. Isn't it? |
@hanumanthan @gaearon Here is another citation form dan which makes us doubt what he meant in above quote:
However as we saw with that code snippet, where you clicked + button, despite two independent event handlers were called, the state went out of sync (e.g. they weren't called consecutively as dan says above in the same event handler - and still went out of sync). Would be nice if dan can clear all this up. |
@gaearon Dan, say I am inside some function, how can I get current version of state? Assume I just want to get current version of state - just to read it, not modify. |
There's some discussion about this in #122. Particularly, #122 (comment). |
@gaearon Dan can you say why below code prints: 0, 2, 4 on the console (see console.log in render) if I click button twice, instead of 0,1,2,3,4?
It seems in this case the prevState you receive in the functional setState is not always the state rendered on the UI |
I assume you see that you're setting state twice in your incrementCount function, therefore increasing count by a magnitude of 2 rather than 1 each time that function is called. React doesn't actually re-render until event-handling is completed, which is why setState can be called twice in your function before re-rendering. |
Yeah for a moment one could think react would call render, after first call to functional set state. apparently it doesn't. |
Wrote some thoughts about why React works this way here: #11527 (comment) |
Docs don't make following clear.
Imagine I have
What will be output above?
also in this case
What will be output now?
The text was updated successfully, but these errors were encountered: