-
Notifications
You must be signed in to change notification settings - Fork 24
Description
This is more of a topic for https://github.com/reactwg/async-react but since it's a closed group I figured I should try to ask here.
I have a local fork of this demo in which I force to always use the HistoryRouter: voluntadpear/async-react@main...version-using-prev
Here is a video showing how setting a search value and changing tabs work correctly. The final state is reflected correctly, with the search input containing my search value and the tab being the one I clicked:
with-prev-value.mp4
However, if I modify this state update:
async-react/src/router/index.jsx
Lines 162 to 181 in c971b2f
| setRouterState((prev) => { | |
| const newParams = { ...prev.search }; | |
| if (value !== "") { | |
| newParams[key] = value; | |
| } else { | |
| delete newParams[key]; | |
| } | |
| return { | |
| url: prev.url, | |
| search: newParams, | |
| pendingNav() { | |
| const newUrlParams = new URLSearchParams(newParams).toString(); | |
| window.history.pushState( | |
| {}, | |
| "", | |
| prev.url + (newUrlParams ? `?${newUrlParams}` : ""), | |
| ); | |
| }, | |
| }; | |
| }); |
and instead of using an updater function in the set function I grab the current state directly and just invoke the set function with the final transformation, as done in this other branch: voluntadpear/async-react@main...version-not-using-updater, this is the behavior observed:
without-updater.mp4
The value I entered into the search input is lost, as routerState doesn't reflect the state update done inside the transition.
This is a confusing behavior since per the docs of useState:
Is using an updater always preferred?
You might hear a recommendation to always write code like
setAge(a => a + 1)if the state you’re setting is calculated from the previous > state. There is no harm in it, but it is also not always necessary.In most cases, there is no difference between these two approaches. React always makes sure that for intentional user actions, like clicks, the age state variable would be updated before the next click. This means there is no risk of a click handler seeing a “stale” age at the beginning of the event handler.
However, if you do multiple updates within the same event, updaters can be helpful. They’re also helpful if accessing the state variable itself is inconvenient (you might run into this when optimizing re-renders).
If you prefer consistency over slightly more verbose syntax, it’s reasonable to always write an updater if the state you’re setting is calculated from the previous state. If it’s calculated from the previous state of some other state variable, you might want to combine them into one object and use a reducer.
I couldn't find any clear indication of what the behavior during a transition should be, and it looks to me the docs indicate that most of the time reading the current state directly should be fine. In my case. I believe the change I did doesn't fall into those mentioned in the docs as potentially problematic.
Could you perhaps shed some light about what's the underlying reason the updater is needed here and the state read is stale during the transition?