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

[Umbrella] Releasing Suspense #13206

Open
acdlite opened this issue Jul 13, 2018 · 100 comments
Open

[Umbrella] Releasing Suspense #13206

acdlite opened this issue Jul 13, 2018 · 100 comments

Comments

@acdlite
Copy link
Member

@acdlite acdlite commented Jul 13, 2018

Let's use this issue to track the remaining tasks for releasing Suspense to open source.

Initial release (MVP)

Core

  • API to read context from within any render phase function (@acdlite) [#13139]
  • Hide timed-out content instead of deleting it (@acdlite) [#13120]
  • Automatic injection of context providers per React root (@acdlite) [#13293]
  • Remove unstable_ prefix from AsyncMode (maybe?)
  • Support for synchronous thenables, and for promises that resolve before the render phase is complete.
    • Confirm that a synchronous thenable that throws an error is handled correctly
  • Confirm it works with <div hidden> [#13089]
  • Why does clicking on several detail links in the fixture one by one eventually causes a big placeholder even if I wait for each of them for less than the placeholder delay before clicking the next one (see tweet)?

Simple Cache Provider

Code splitting

  • Support promise as a component type
  • (maybe) Open source lazyLoadComponent?

Test renderer

  • Finalize public APIs for flushAll, yield, etc
    • Tentative plan is to publish custom matchers for each of the major testing frameworks, a la #13236.

Docs

  • Blog post
  • React.Placeholder
  • simple-cache-provider
  • Unnamed code-splitting library

Follow ups

Soft expiration (#14248)

  • Implement an API for in-place loading indicators that aren't ancestors
  • Make sure there's a way to avoid flashing the inline spinner if it's fast enough

Streaming server renderer

  • Implement a streaming server renderer like the one in @acdlite's ZEIT talk
  • Partial hydration

Related: Time Slicing Umbrella (#13306)

@aweary
Copy link
Collaborator

@aweary aweary commented Jul 13, 2018

Expose unstable_AsyncMode (maybe?)

Isn't this already exposed?

@acdlite
Copy link
Member Author

@acdlite acdlite commented Jul 13, 2018

I meant remove the unstable_

@thoamsy
Copy link

@thoamsy thoamsy commented Jul 15, 2018

I am looking forward to open source of the unnamed code-splitting library 💯

@ryota-murakami
Copy link
Contributor

@ryota-murakami ryota-murakami commented Jul 23, 2018

What does it mean [Umbrella]?🤔☂️

@ghoullier
Copy link

@ghoullier ghoullier commented Jul 23, 2018

This mean, it's a feature which impact several projects/packages/tools.

@ryota-murakami
Copy link
Contributor

@ryota-murakami ryota-murakami commented Jul 23, 2018

@ghoullier I see, Thank you so much!

@JedWatson
Copy link
Contributor

@JedWatson JedWatson commented Jul 24, 2018

Hey @acdlite, just a question about how to best prepare for this. Not asking for / expecting any kind of timeline, but wondering:

Are you currently expecting these features to drop into React 16 and be easy to adopt incrementally, like the new Context API that landed with 16.3?

Or are you thinking it'll be something that pushes React to v17 and require more work to adopt?

Asking because I'm working on a roadmap that crosses over significantly with pretty much everything on your list and am trying to work out how to best deal with that.

Also do you have any tips on how to best prepare (in terms of code written today, that wants to be future compatible with these improvements to React) - polyfills / techniques / etc?

(apologies if these questions are answered elsewhere and I've missed them)

@donaldpipowitch
Copy link

@donaldpipowitch donaldpipowitch commented Jul 24, 2018

Adding another question to @JedWatson's questions:

  • We also don't need/expect to get a timeline for a stable release, but would it be possible/useful to get a new prerelease? (AFAIK the newest release is 16.4.0-alpha.0911da3 from February.)

Thank you! ❤️

@NE-SmallTown
Copy link
Contributor

@NE-SmallTown NE-SmallTown commented Jul 24, 2018

IMO, they will provide a blog post like before before it's been landed.

And I think you don't need to prepare too much because there is no breaking change(it does have many features that maybe would seems different/conflict with current practices, like redux fetch with suspense, but there will be a codemod or easy encapsulation to do this, you know, fb has 3W+ components). And if you watch the talk of @acdlite (about ssr suspense in ZEIT) and @gaearon (about client suspense in iceland), you will know you don't need to worry about too much and it's not invasive.

By the way, you can just search the key 'Umbrella' in the repo and you will find more info like #8830 and #12152

AFAIK the newest release is 16.4.0-alpha.0911da3 from February.

IIRC, this is a misoperation?

@cyan33
Copy link
Contributor

@cyan33 cyan33 commented Jul 24, 2018

I'm working on rolling out the suspense module and new APIs in facebook. In case @acdlite is busy with something else, I'd like to share some of my thoughts of our experience in facebook and answer some questions of @JedWatson.

Are you currently expecting these features to drop into React 16 and be easy to adopt incrementally, like the new Context API that landed with 16.3?

I'm not sure if it will come with React 16 or 17. According to the React team, it's likely to be released before the end of this year, which depends on how well it runs in facebook and how the related API is ready or not. But code-wise, I'm happy to say that it would be easy to adopt, because we've been experimenting for quite a while in facebook. The suspense feature will still work for the existing codebase. But with additional changes (like async rendering), you'll have more bonus that the new feature will bring you.

Do you have any tips on how to best prepare (in terms of code written today, that wants to be future compatible with these improvements to React) - polyfills / techniques / etc?

I'd say the migration is rather incremental and progressive. Like @NE-SmallTown said, we don't want to introduce any breaking changes. That would also be painful to roll out to facebook because we have a so large codebase. But so far, the roll out has been smooth and doesn't require you to do additional changes.

@acdlite
Copy link
Member Author

@acdlite acdlite commented Jul 24, 2018

@JedWatson

Are you currently expecting these features to drop into React 16 and be easy to adopt incrementally, like the new Context API that landed with 16.3?

Incrementally. Always incrementally :) Otherwise there's no way we'd be able to ship this at Facebook.

Here's what I'm expecting:

Client Server-side rendering
Suspense Works everywhere* Same constraints as existing server renderer
Async rendering Opt-in using <AsyncMode> Same constraints as existing server renderer

*In sync mode, delayMs is always 0. Placeholders show up immediately.

Suspense will work without any changes to your existing components. At one point we thought we might require <StrictMode> compatibility, but during our internal testing we discovered one of the best ways to upgrade to strict mode was to use Suspense. Chicken-egg dilemma. So we found a way to make it work even outside of strict mode.

So the idea is that users will start migrating to Suspense even before they're ready to migrate to asynchronous rendering. Then once a subtree is ready, they can opt-in by wrapping in <AsyncMode>.

For new apps, though, the story is different: go async by default. We'll introduce a new root API (a replacement for ReactDOM.render) that is async only.

There will be an awkward period after the initial release where many third-party frameworks (Redux, Apollo, React Router...) may not work properly in async mode. That might hurt adoption for a while. But the idea is that the new features will be so compelling that it won't take long for libraries to either adapt or be superseded by an async-compatible alternative.

Also do you have any tips on how to best prepare (in terms of code written today, that wants to be future compatible with these improvements to React) - polyfills / techniques / etc?

Wrap everything in <StrictMode> and make sure there are no warnings. We'll have more detailed migration guides as we get closer to release.

@hwillson
Copy link

@hwillson hwillson commented Jul 25, 2018

There will be an awkward period after the initial release where many third-party frameworks (Redux, Apollo, React Router...) may not work properly in async mode.

Apollo doesn't do awkward - we'll be ready! 🕺😳

Seriously though, we ❤️ all things React, so making sure we're in-line with these changes for the initial release is not only a high priority, but it's also something we're super excited about! Thanks for all of your amazing work on this @acdlite!

@markerikson
Copy link

@markerikson markerikson commented Jul 25, 2018

I'll chime in and say that the Redux team is working on async compat for React-Redux.

I laid out a potential roadmap at reduxjs/react-redux#950 . TL;DR:

  • React-Redux 5.1 will hopefully work with <StrictMode> with no warnings (current PR: reduxjs/react-redux#980 )
  • 6.0 will be an internal rewrite to use the new context API, add ref forwarding, and possibly other changes, but try to keep as much of the current public API as possible (ie, <Provider> and connect() ). We'll see how well that works with async rendering, and figure out the best path forward. (My prior proof-of-concept PR is at reduxjs/react-redux#898 , but we'll probably redo it based on other lessons learned from the 5.1 work.) It's likely that this release would require React 16.5 as a minimum, due to the need for new context and probably also the as-yet unreleased "read context from lifecycle methods" PR that was just merged.
  • After that, we're open to ideas for a different React-Redux API (yes, yes, that possibly includes render props, people).

We'd appreciate more eyes on our WIP, and hopefully people can give us some more feedback and discussion on how they're looking at using Redux with React Suspense and async rendering so we can make sure use cases get covered properly. We're also hoping to have some more discussions with the React team about exactly what constraints we need to work with, and it'd be helpful if we could get some sample apps that would let us see what problems we need to solve for all this to work correctly.

@anymost
Copy link

@anymost anymost commented Jul 26, 2018

looking forward to the release of Async rendering and Suspense

@ghost
Copy link

@ghost ghost commented Jul 27, 2018

@acdlite Also question about suspense and async rendering.
My question is once they are introduced and one starts writing apps with that new version of react: does it mean that react API and the way people code in react will change too? (even if they don't plan to use features of suspense and async rendering?)

I assume it can be trickier to write react code with suspense and async rendering (maybe due to some new API or other constraints), and for those who don't need it, why force them to use react in a new way? And not allow them to code in react the way they do now?

@gaearon
Copy link
Member

@gaearon gaearon commented Jul 27, 2018

I assume it can be trickier to write react code with suspense

Have you had a chance to watch the second half of my talk? I'd say quite the opposite — it's way less trickier to use suspense for data fetching than anything else (including Redux, local state, or some other library).

@ghost
Copy link

@ghost ghost commented Jul 27, 2018

@gaearon I haven't. I was speaking more in theory. Imagine there is already set of people who know react. If people don't need the feature of async rendering and suspense, why force them learn "new" react? Especially if the "new" react is tricker to use? But: I am not well informed so I might be wrong say about the "trickier" part - I am just sharing some of my thoughts :).

In a way I am saying if 10% of apps need the feature of Suspense and async rendering, why in those other 90% cases force people to learn "new" react? But again I might be wrong, since I didn't gather much info about suspence and async rendering yet.

@gaearon
Copy link
Member

@gaearon gaearon commented Jul 27, 2018

I think it's hard to have a conversation if you haven't looked at my demos yet.

To be clear: there's no "new React", these features don't break any existing patterns 🙂. They are additive. You don't need to write code in a completely different way to use those features either — although some of them only work if you use modern lifecycle methods.

While this is not directly related to your concern, I disagree they're "trickier to use". I think suspense is much simpler to use than any other loading mechanism that currently exists. That's the reason I'm so excited about it. But again, you don't have to use any of the new features if you don't want to. Old patterns will keep working.

I really do recommend watching my talk. I'm sure this will make a lot more sense once you see these features in action.

@ghost
Copy link

@ghost ghost commented Jul 27, 2018

@gaearon

All old patterns keep working.

Thanks for feedback Dan. Yeah that is how I thought, I suppose if people don't need those features they should be able to write the way they used to before those features were added.

good luck.

@ghost
Copy link

@ghost ghost commented Aug 18, 2018

Hey Dan(@gaearon), I am not nitpicking but want to figure it out. Above you said:

But again, you don't have to use any of the new features if you don't want to. Old patterns will keep working.

Which would suggest that I can code in new React the same way I did in "old" React, e.g. I could use the life cycle methods in the same way, etc. right?

However, here, bvaughn says that getDerivedStateFromProps (or componentWillReceiveProps) could be called many times for one update, hence his solution not to fetch data inside it.

So my question is, after all, it does seem we can't use the new React in exactly the same way as before right? Because AFAIK in current react componentWillReceiveProps doesn't get called many times for one update, isn't it?

@markerikson
Copy link

@markerikson markerikson commented Aug 18, 2018

@Giorgi-M : yes, the lifecycle methods are changing, but the point is that Suspense itself is an opt-in feature. All your existing React render methods and React's rendering behavior will work as-is. However, if you opt in by adding an <AsyncMode> tag to a part of your app, and you begin using Suspense's approach for indicating async data needs, then you can take advantage of it. None of that happens if you don't add that to your codebase.

@TrySound
Copy link
Contributor

@TrySound TrySound commented Aug 18, 2018

@Giorgi-M componentDidUpdate should be used instead of componentWillReceiveProps or getDerivedStateFromProps.

@ghost
Copy link

@ghost ghost commented Aug 18, 2018

@markerikson So you say that what bvaughn said here, that getDerivedStateFromProps can be called many times for one update, is not necessarily the case, if I haven't enabled the <AsyncMode/>?
(sorry for asking such questions just they popup to me from time to time, and didn't find resource which would cover all).

ps. bvaughn also didn't mention the optionality of that in the linked thread, hence it raised my suspicion.

@pshrmn
Copy link

@pshrmn pshrmn commented Aug 22, 2018

Should a method for enqueueing asynchronous updates (e.g. deferSetState() for class components as opposed to renderer-specific unstable_deferredUpdates()) be added to the core checklist?

From my understanding, any updates for fibers in async mode will be asynchronous, which in theory means that deferSetState() would be unnecessary. However, the unstable-async/suspense demo mixes a synchronous update and an async update and I'm not sure how that can be accomplished in async mode (for "universal" components).

@gaearon
Copy link
Member

@gaearon gaearon commented Aug 22, 2018

It’s in the check list for the time slicing umbrella.

@keshavmesta
Copy link

@keshavmesta keshavmesta commented Aug 27, 2020

@gaearon what's the current state Partial Hydration? I know #14717 was merged but I doubt it made into any release?

@gaearon
Copy link
Member

@gaearon gaearon commented Aug 27, 2020

It's been on in every @experimental release for a long time by now, as long as you use the unstable_createRoot API.

@gaearon
Copy link
Member

@gaearon gaearon commented Aug 27, 2020

@maraisr
Copy link

@maraisr maraisr commented Oct 19, 2020

@gaearon are you able to please elaborate what you mean by "data driven dependencies"?

@gaearon
Copy link
Member

@gaearon gaearon commented Oct 19, 2020

@gaearon are you able to please elaborate what you mean by "data driven dependencies"?

Yes, of course. I must apologize for the brevity of the list above — it's very condensed and many of these are significant separate subprojects (which is why they're taking time).

Let me take a step back and give some broader context before answering your specific question. The broader context is that building a really good data fetching solution is really, really hard. Not just in the sense of the implementation but in the sense of the design. Typically, one would have to make a compromise between colocation (keeping the data requirements close to where the data is used) and efficiency (how early do we start loading the data, and can we avoid network waterfalls). This isn't as noticeable at a small scale, but as the number of components grows you really have to choose between great performance and easy-to-write code. In many cases, unfortunately, you don't get neither — which is why data fetching in general is such a hot topic.

We have a very high bar for what gets into "official" React. To be "Reacty", it has to compose as well as regular React components do. This means we can't in good faith recommend a solution that we don't believe would scale to thousands of components. We also can't recommend a solution that forces you to write code in a convoluted optimized way to keep it performant. At FB, we've learned a lot of lessons in this from the Relay team. We know not everybody can use GraphQL, or would want to use Relay (it's not by itself very popular and the team hasn't optimized it for external adoption). But we want to make sure that our data fetching solution for general React incorporates the hard-earned lessons from Relay and doesn't suffer from having to choose between performance and colocation.

I want to emphasize this isn't just about big apps. The problems are most noticeable in big apps, but small apps that import a bunch of components from npm also suffer from a subset of these inefficiencies. Such as shipping too much client-side code and loading it in a waterfall. Or loading too much upfront. Also, small apps don't stay small apps forever. We want a solution that works great for an app of any size, just like the React component model works the same way regardless of your app's complexity.

Now, to address your specific question. Relay has a feature called "data-driven dependencies". One way to think about it is an evolution of dynamic import. Dynamic import is not always efficient. If you want to load some code only when a condition is true (e.g. "is user logged in" or "does user have unread messages"), your only option is to trigger it lazily. But this means you are "kicking off" fetching (such as with React.lazy) only when something is used. That's actually too late. For example, if you have several levels of code-split components (or components waiting for data), the innermost one would only start loading after the one above it has loaded. This is inefficient and is a network "waterfall". Relay "data-driven dependencies" let you specify the modules you want to fetch as a part of the query. I.e. "if some condition is true, include this code chunk with the data response". This lets you load all the code-split chunks you're going to need as early as possible. You don't have to trade colocation for performance. This might seem like not a big deal but it shaved off literal seconds in the product code.

Now, again, we're not putting Relay into React, and we don't want to force people to use GraphQL. But conceptually the feature itself is generic, and having a good open source solution for it would let people do a lot more code splitting than is done today (and thus ship a lot less client JS code!) That generic feature won't be called "data driven dependencies" — that's just a Relay name I referred to. This feature will be a part of a larger recommended solution that doesn't require GraphQL. I just referred to it by that name in the list because this was a lot to explain for a single bullet list point.

I hope this clarifies it.

@gaearon
Copy link
Member

@gaearon gaearon commented Dec 21, 2020

We've made more progress on this.

https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html

@gaearon
Copy link
Member

@gaearon gaearon commented Mar 24, 2021

It’ll soon be a year after #13206 (comment) so I want to provide a small update.

There’s an initial version of this but we haven’t tested it widely yet. We’ll focus on this more after the initial React 18 release candidate is out.

  • Changing the priority model to a more sensible one. (in progress @acdlite, #18612)

This is done and fixed a dozen of bugs.

  • Only allow the last pending transition to finish. (in progress @acdlite)

The important part here is done. There’s a related fix for pending indicators that we’re going to punt on for the initial release because it’s less important but will fix in a follow-up.

This uncovered a need for a significant refactor that took us several months. The refactor is done now but we’ll not include the feature itself in the initial release.

  • Fire effects when hiding/showing content for Suspense

This is coming very soon. That’s one of the last significant changes to the model we want to make before stabilising. (It wouldn’t be possible to do without the above refactor.)

  • Show and hide Portal children when needed

Won’t do this. Instead the previous change lets you control it yourself.

  • Align with the ongoing standardization work for Scheduling (not started)

We’ve made a lot of progress here and the layering mostly makes sense now. Might be some other non-blocking work here.

This is mostly done.

  • Delegate to roots instead of document to enable more gradual adoption (in progress, @trueadm)

This was the sole focus of 17.

  • Flush discrete events in capture phase.

I’m not sure what this referred to but I think it’s either done or no longer needed.

  • Consider getting default priority from event to play nicer with imperative code.

We just did this.

Changing the priority model mostly fixed those. Some of the remaining other ones disappeared after the refactor that was initially prompted by Offscreen work. Yet more disappeared after changing the event defaults. I’d say we’re good here.

  • Finalize other API semantics and defaults. (not started)

That’s one of the remaining areas to polish.

  • A solution for non-GraphQL use cases (in progress @sebmarkbage in collaboration with Next.js).

This is basically Server Components. Work in progress but doesn’t block the release.

  • A generic caching solution. (not started)

We have the first implementation but more API design and implementation tweaks might be needed. This is needed for Server Components too. But not the initial release necessarily.

Ecosystem compatibility and good defaults. It doesn't help if we release something that nobody can use today because it doesn't work with their libraries or existing approaches.

This is active ongoing work.

Implement a streaming server renderer like the one in @acdlite's ZEIT talk

The client hydration support for this was done well over a year ago. The actual streaming SSR development has now started and is in active development. #20970

We see the light at the end of the tunnel. This time it’s closer. 🙂 Hang on.

@Andarist
Copy link
Contributor

@Andarist Andarist commented Mar 24, 2021

Fire effects when hiding/showing content for Suspense

This is coming very soon. That’s one of the last significant changes to the model we want to make before stabilising. (It wouldn’t be possible to do without the above refactor.)

Just to confirm - is this the one about cleaning up effects in hidden trees and refiring them when the content gets shown again? Is there a specific plan for how you will roll out this? I suppose this will come behind a flag at first but what's next? Is there a plan to just enable this in React 18?

Do you have any plan/guidance for effects that should be tied to the overall lifecycle of the component? Like for example, it might not make sense to fire some network requests when showing a component again. I guess that the most common answer is to hoist such request but since the Suspense and "suspender" component can be far away from each other so the components in the middle might not easily know about this.

@bvaughn
Copy link
Contributor

@bvaughn bvaughn commented Mar 24, 2021

Just to confirm - is this the one about cleaning up effects in hidden trees and refiring them when the content gets shown again?

@Andarist Yes. I plan to post a PR with an initial implementation of this (for Suspense and layout effects only) today.

Do you have any plan/guidance for effects that should be tied to the overall lifecycle of the component?

We will provide guidance in the form of documentation and coding examples in the coming weeks. We began rolling out these new effects semantics internally within Facebook to test the concept several weeks ago.

@Andarist
Copy link
Contributor

@Andarist Andarist commented Mar 24, 2021

@bvaughn Great, gonna be looking forward to this PR and discussion/documentation 👍

@gaearon
Copy link
Member

@gaearon gaearon commented Mar 28, 2021

Ecosystem compatibility and good defaults. It doesn't help if we release something that nobody can use today because it doesn't work with their libraries or existing approaches.

This is active ongoing work.

I should probably expand a bit more on this.

We don't have large "unknown unknowns" anymore. So the remaining work is mostly about preparing the ecosystem for migration. You might have noticed that we pay attention to incremental migration on the React team. So we want to get this right. Incremental migration, including finding the right tradeoff, writing migration guides, and working with the ecosystem library community, are just as important as implementing new features.

Originally the work on Concurrent Mode started with theory. We knew that it makes sense in principle. For example, that you shouldn't continue doing a computation when it is no longer needed (#17185 (comment)). We started with this one use case but it turns out the same constraints unlock a whole range of features. For example, we didn't plan Suspense at all in the beginning. Suspense came out of a streaming server renderer exploration. But it turned out that Concurrent Mode solves the issues with streaming server rendering too. (Such as letting us start hydration before all the client code is loaded.) We didn't plan Server Components back then, but it turns out Concurrent Mode solves issues there too. (Such as letting us render them while they stream in instead of waiting for the whole response.) This is what happens when you start with a solid theory. As much as this metaphor might seem preposterous in the context of front-end development, it's a lot like math. If the principle is right, concrete applications for that principle will keep coming up. Even the ones you didn't anticipate in the beginning.

Building it went in full swing around the same time as FB started a website rewrite. We latched onto the opportunity to dogfood it and stress-test it there. It was a wild ride, since React is the foundation on top of which a lot of other infra and product code is built. And of course our initial guess had many mistaken assumptions and flaws in it. Not many people realize that Concurrent Mode was controversial at FB. In the beginning of the website rewrite, many issues were attributed to it — some correctly and some not. We kept iterating and fixing both the API and the implementation. It was a lot of hard work. There was an inflection point about a year and a half ago when people no longer wanted to remove it. It started delivering wins. The flawed APIs were replaced, and the product engineers weren't confused anymore. Implementation quirks and bugs were squashed, so the stability was not a concern anymore. We added more missing puzzle pieces (and are still adding them), so the whole picture started to take shape and people could see why all of this effort was worth it. Sure, maybe there was a less risky way to prove out a technology than to rebuild a website on it before it's really "ready", but it helped us catch all the flaws that we'd have to spend many years catching if we were to make it stable in open source at that time.

We know that it works now. In fact, removing it would be a huge regression. But not everyone can rewrite their website. And we can't either — our website is only a small part of our web-based code. In addition to Ads (which drives the revenue), there's a myriad internal tools that would never be rewritten. So over the past six months, we have shifted our focus from putting out fires and fixing flaws (which we're running out of) to working on the incremental migration strategy. An incremental migration strategy encompasses many aspects. It means removing implementation complexity where it wasn't warranted if it leads to simpler behavior. It means giving you the lever to opt into new behavior instead of turning it on by default. (For example, we are making most kinds of updates synchronous by default, negating many concerns about concurrency, unless you use a particular API that opts into concurrency where you want it.) It means writing migration guides. It means surveying the ecosystem libraries for patterns that might break, talking to their maintainers, and figuring out the adoption strategy. It means polishing the release candidate so that we can rotate fully to ecosystem support. All of this is important work that takes time.

That work is most of what we're busy with these days. Removing, simplifying, streamlining, and preparing.

@gaearon
Copy link
Member

@gaearon gaearon commented Mar 28, 2021

By the way. If someone discovers this thread from some Twitter argument, I want to be explicit that we've made a decision to not participate in those. These arguments are draining, don't lead to anything productive, tend to pop up on weekends, are ruining our work-life balance, tend to be ill-informed, don't afford nuance or context, and are largely a waste of time for all parties involved. We've never shied away from explaining our position in a longer-form medium such as here on GitHub, and I hope this space stays healthy that we can continue doing so.

There is definitely an appetite for more frequent updates on the state of our research before it hits stable. Server Components announcement was a pilot, and given its warm reception, we have a few more things in the pipeline to address that appetite.

But if we have to choose between talking about our guesses and pontificating, versus working on things that actually bring the stable version closer (including all the ongoing work to enable incremental adoption), we've learned enough to choose the latter.

@dfabulich
Copy link

@dfabulich dfabulich commented Mar 29, 2021

In the Server Components FAQ, https://github.com/reactjs/rfcs/blob/2b3ab544f46f74b9035d7768c143dc2efbacedb6/text/0000-server-components.md#why-not-use-asyncawait the React team said that they would prioritize an RFC for Suspense in early 2021, documenting why Suspense-compatible data-fetching APIs have to throw Promise objects in order to suspend, instead of using standard async/await patterns.

I continue to think that there's no way to write that RFC: you'll find that the argument for throwing Promises falls apart when you try to explain it.

This is the big remaining "known unknown" in Suspense's API and design. I'm especially worried to hear y'all say "We don't have large "unknown unknowns" anymore" when IMO it's inevitable that this design will require significant changes during the RFC process.

@Andarist
Copy link
Contributor

@Andarist Andarist commented Mar 29, 2021

I continue to think that there's no way to write that RFC: you'll find that the argument for throwing Promises falls apart when you try to explain it.

Given this design has already years and they had to discuss this extensively between each other and with other people inside and outside of Facebook I find such a statement rather ill-placed. Do you really believe that a team of those engineers can't explain their choices? You/we/whoever don't have to agree on any particular choice taken but to state that the choice is so bad that it can't even be explained is derogatory to the other party.

One of the reasons that immediately comes to my mind is that with async/await you have to opt-into asynchronicity and you can't make a synchronous call so scheduling things becomes much harder and often less predictable. For instance, redux-saga in the past had a Promise-based scheduler but it was that hard to maintain it without bugs that it has been rewritten to a simpler one that is using callback-based continuations, allowing for synchronous resuming. So I totally understand the design choice here - even if it looks a little bit funky at first.

@gaearon
Copy link
Member

@gaearon gaearon commented Mar 30, 2021

A brief answer is that we needed a pattern that can work both on the server and the client without writing code differently — so that it works e.g. in Shared Components. Async/await inside components gets pretty bad on the client for multiple reasons, including (1) unnecessary waterfall of ticks in the most common — already resolved — case, (2) it does not solve caching so you have to add a caching layer anyway, (3) continuations will be obsolete by the time they run since props/state often would have changed. I'd rather not derail this thread by going in depth on this topic yet (you're right we still need to write it up, although right now we're not yet focusing on this).

@dfabulich
Copy link

@dfabulich dfabulich commented Mar 30, 2021

I certainly agree that this thread isn't the place to hash out the Suspense RFC and async/await issues, but I would like to constructively request that publishing and discussing the Suspense RFC be a priority.

This issue is about releasing Suspense, and the major prerequisites for releasing Suspense. Discussing Suspense with the public in an RFC, making sure you're actually shipping the right thing, is (or should be) a major prerequisite for releasing Suspense.

As it stands, the team is focused on releasing Suspense, but "not yet focusing" on discussing Suspense in an RFC, as if the RFC discussion has very little to do with releasing Suspense.

I'm very concerned that y'all think there's probably little to be gained from public feedback, so the RFC can be put off until the end, when Suspense is already basically done and ready to ship. But pushing the RFC off until later will just make it harder to change direction if/when that becomes necessary.

@gaearon
Copy link
Member

@gaearon gaearon commented Mar 30, 2021

I think it’s fair to say that having used this mechanism for the last two years heavily in production, we feel rather confident about it and understand its tradeoffs well. This doesn’t mean feedback won’t be welcome, but we need to be practical with sequencing how we spend our time. The exact API isn’t central to the paradigm, but the constraints that led us to it are, and any alternative solution would have to satisfy the same constraints. In terms of a concrete solution, if there is a better one that satisfies them, we could replace it — this is the edge of the system so it’s not difficult aside from figuring out a migration path for things already relying on it. However, if the constraints themselves are something we disagree on, or the desired feature set, it’s a difference that an RFC would be unlikely to resolve. If your concern is that the more time we spend missing some fundamental flaw, the harder it would be to turn around, I think now that we’re in a final stretch of work, there is not much difference a delay of a few months would make. That said, I’ll discuss this with the team to see if we can prioritise writing it.

@gaearon
Copy link
Member

@gaearon gaearon commented Apr 1, 2021

Today, we're announcing React Labs — a new video series with technical deep dives with the React team members. Our first video is a Server Components Architecture Q&A deep dive. We hope you enjoy it!

@gaearon
Copy link
Member

@gaearon gaearon commented Apr 7, 2021

Fire effects when hiding/showing content for Suspense

This is coming very soon. That’s one of the last significant changes to the model we want to make before stabilising. (It wouldn’t be possible to do without the above refactor.)

This landed in #21079. We will be testing this internally in the coming weeks.

Compatibility solution for Flux-like libraries. (in progress @bvaughn, reactjs/rfcs#147)

There’s an initial version of this but we haven’t tested it widely yet. We’ll focus on this more after the initial React 18 release candidate is out.

The next item on the list makes it less urgent, by the way:

Ecosystem compatibility and good defaults. It doesn't help if we release something that nobody can use today because it doesn't work with their libraries or existing approaches.

This is active ongoing work.

#21072 makes Time Slicing opt-in, which means that if you don't use new features like useTransition or useDeferredValue or <Suspense> refetches, even mutable Flux-like solutions continue to work the way they're written today. We'd still want to offer more first-class support to state management libraries that want to take advantage of the new features, but this change should help resolve a lot of past concerns about the initial upgrade. This has not landed yet, but hopefully this week.

Implement a streaming server renderer like the one in @acdlite's ZEIT talk

The client hydration support for this was done well over a year ago. The actual streaming SSR development has now started and is in active development. #20970

We're hoping to reach feature parity with the existing server renderer soon. #21153 landed a few days ago, and there's more coming.

@samcooke98
Copy link

@samcooke98 samcooke98 commented Apr 7, 2021

With regard to:

Ecosystem compatibility and good defaults. It doesn't help if we release something that nobody can use today because it doesn't work with their libraries or existing approaches.

Is there anything the community can do to help?

At the company I work at, we're heavy users of MobX, and so we would be keen to help figure out how Concurrent Mode \ Time slicing can work with MobX. I imagine there are similar companies out there as well.

@gaearon
Copy link
Member

@gaearon gaearon commented Apr 7, 2021

MobX is a tricky case because it diverges from our direction. We want it to work but we can't make it benefit from features built on the opposing principle.

Like, there's no amount of magic that can make an approach fundamentally taking advantage of immutability ("we can render two versions of state independently") fully compatible with an approach fundamentally built on mutability ("changing this property immediately propagates"). I think the most we can count on here is not breaking the apps using it, which we'll be trying our best to do during the community rollout phase. But there's nothing we can do to make it benefit from new features that are built on (and enabled by!) the very opposite principle. That's the nature of picking a tradeoff.

That said, in practice it depends on how much mutation is happening and at what stage. E.g. Relay happens to rely on mutation in its implementation details today, and although technically it's breaking the rules, it works fine at scale because its updates aren't super common (mostly network responses), and we have some fallback mechanisms to recover from failures. We'd like to offer some APIs for mutable stores to integrate better with React, first with deopts like useMutableSource, but maybe later with some more first-class alternative mutable pattern (for example, databases solve this with MVCC) which could work just as well as immutability. I don't know at this point how it would look like or if MobX could take advantage of that to support new features well.

@gaearon
Copy link
Member

@gaearon gaearon commented Apr 7, 2021

As for a broader question, there is definitely a lot the community can do to help once we figure out the full upgrade story. I realize there's been a lot of annoyance with us not publishing some kind of "upgrade guides" earlier, but the whole point is we don't know the full story yet and we're still changing it so that it makes more sense. We don't want to churn people without a really good reason. I'm literally writing an internal guide now that we'll be using to migrate legacy surfaces to Concurrent roots, even the ones relying on Flux and mutation. We'll see if that goes well. Once we try those out, we'll use them as a basis for the open source guides. We hope to publish this information together with a release candidate, at which point we'll rotate fully to supporting open source libraries and helping them overcome any difficulties and common issues with migration. At that stage, we expect to gather some common recipes, and we'll definitely welcome (and need!) the community's help to propagate the knowledge and common solutions through the ecosystem. Although, like I said, a bulk of our current work is to make as much as possible just work out of the box — at least until you start using new features. Then you get to decide whether these new features are worth it, both as an application user and as a library author who's considering whether to invest some time in following the rules better.

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

Successfully merging a pull request may close this issue.

None yet