Skip to content
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

Discussion: Synchronous State Management and Async React #13286

Closed
markerikson opened this issue Jul 29, 2018 · 3 comments
Closed

Discussion: Synchronous State Management and Async React #13286

markerikson opened this issue Jul 29, 2018 · 3 comments

Comments

@markerikson
Copy link
Contributor

markerikson commented Jul 29, 2018

The React team has put out a fair amount of info on the upcoming async React concepts, including time sliced rendering and Suspense. They've also been in contact with the various state management library teams (Redux, MobX, and Apollo) to give us an early heads-up on how these libraries might need to change to work correctly with async React.

At this point I personally feel like I still have only a partial understanding of what types of changes are needed. Quoting a discussion between Dan and myself in reduxjs/react-redux#898 (comment) :

To the best of my understanding, these are the problems that React-Redux faces when trying to work with async React:

  1. React's time-slicing means that it's possible for other browser events to occur in between slices of update work. React might have half of a component tree diff calculation done, pause to let the browser handle some events, and something else might happen during that time (like a Redux action being dispatched). That could cause different parts of the component tree to read different values from the store, which is known as "tearing", rather than all components rendering based on the same store contents in the same diff calculation.
  1. Because of time-slicing, React also has the ability to set aside partially-completed tree diffs if a higher priority update occurs in the middle. It would fully calculate and apply the changes from the higher-priority change (like a textbox keystroke), then go back to the partially-calculated low-pri diff, modify it based on the completed high-pri update, and finish calculating and applying the low-pri diff.
    In other words, React has the ability to re-order queued updates based on priority, but also will start calculating low-pri updates as soon as they're queued. That is not how Redux works out of the box. If I dispatch a "DATA_LOADED" action and then a "TEXT_UPDATED" action, the data load causes a new store state right away. The UI will eventually look "right", but the sequence of calculating and applying the updates was not optimal, because Redux pushed state updates into React in the order they came in.

There was also some relevant discussion on Twitter at https://twitter.com/dan_abramov/status/1010978574105567234 :

swyx: so if anything async react “absorbs into the platform” some tricky parts of state mgmt (more accurately, creates a priority queue for UI interactions, and pushes async deps to whatever cache lib people end up using). haha, i guess im too eagerly taking those for granted.

dan_abramov: yeah. For the best desired experience, it needs control over when to apply updates, and what version of state to render with. So if a lib wants to own that, it’s missing out (although could work by getting “deoptimized” to sync mode).

mweststrate: For inspiration: Is there an abstract write down / complete overview of which conceptual operations need to be supported to fully support async? Fork + replay changes made in forks suffices? (hints about conflicts resolution?)

dan_abramov: Paging acdlite who’s currently working out how to make Relay work with it
dan_abramov: I think the main idea is that either your thing is meant for UI state that changes with interactions (and then we need a way to “route” it through setState—so it needs to provide a reducer), or it's more like a data cache (in which case your own storage is fine if it’s immutable)
dan_abramov: There is a third option too (a mutable data cache) which is what Relay will end up being for now. But this means it “deopts” to sync mode in some cases.

sebmarkbage: Those are more downstream recommendations, not first principles. A first principles is that you need to be able to read consistent old versions so if a parent passes data, the child needs to be able to read the version that the parent passed even after it has changed later.
sebmarkbage: Another first principle is that you need to be able to make edits to old versions and also make the same edit on the latest version (rebase).

dan_abramov: How would you do it without letting React manage state for you? We don't explicitly tell libs “now is time to rebase”.

mweststrate: Would need some kind of hooks were React tells: I want to fork / rebase this prop?

The phrase "deopts to sync mode" was explained by Brian in #13186 (comment) :

State updates scheduled from componentDidMount or componentDidUpdate are processed synchronously and flushed before the user sees the UI update. This is important for certain use cases (e.g. positioning a tooltip after measuring a rendered DOM element). In the case we're describing, this means that users of your application will never even see the temporary stale value because React will process the new value (synchronously) before yielding.

That might sound like a good thing, but what if the re-render includes a lot of components or is slow for some other reason? Then it might impact the frame rate and cause your application to feel unresponsive. This is what we are referring to when we say that create-subscription de-opts to synchronous rendering mode in some cases.

Also, Andrew commented in https://twitter.com/acdlite/status/1015286450537951233 :

We've learned from our work making Relay interop with async React, will share more soon.

Finally, there's some related discussion in reduxjs/react-redux#890 .

So, at this point what I would particularly appreciate is further info on exactly what constraints a normally-synchronous state management lib like Redux or MobX needs to comply with in order to work correctly with async React behavior, and any suggestions on possible implementations. It would also be extremely beneficial if we could come up with some demo apps that specifically demonstrate problematic interactions between async React and synchronous state management logic, so that we can use those as points of reference for ensuring that new versions of our libraries work correctly going forward.

Paging @gaearon , @acdlite , @bvaughn , @timdorr , @jimbolla , @cellog , @mweststrate, @peggyrayzis , and @jbaxleyiii for their thoughts and participation.

@gaearon
Copy link
Collaborator

gaearon commented Aug 2, 2018

Hey @markerikson!

Don't want to disappoint you, but we've tried GitHub issues, chats, and video calls, and it still doesn't feel like the discussion is moving forward. I think this is mostly because all of these things sound too abstract, and to get a better idea one needs to play with these features — there's no substitute to that.

We're working hard on getting these features closer to a state where they're usable enough that you can start experimenting with them. I think this is the most important thing we can do now — any discussions without practical demos, as we've already seen, tend to go in circles.

So I'm asking that you please give us more time to prepare. When we're ready, we'll publish some examples with more detailed explanations of the behavior. So you'll be able to see what we mean exactly in each case. I think that once you try to implement the same behavior (e.g. with Redux) you'll see the issues we're trying to solve by yourself, and at that point the discussion will be significantly more productive. Until we're at that point, I'm afraid we don't have more to offer yet.

Sorry to keep you waiting!

@gaearon gaearon closed this as completed Aug 2, 2018
@markerikson
Copy link
Contributor Author

Yeah, I think that's basically what I'm asking for :) Demos that show off difficult patterns, explanations of where the pain points are, etc, so that we've got something concrete to compare against.

@gaearon
Copy link
Collaborator

gaearon commented Aug 3, 2018

Here's something as a starting point: https://mobile.twitter.com/dan_abramov/status/1025185531515351040

We'll add more examples and explanations as it gets more solid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants