mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
7cd98ef2bcbc10f164f778bade86a4daeb821011
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bf449ee74e |
Replay Client Actions After Hydration (#26716)
We used to have Event Replaying for any kind of Discrete event where we'd track any event after hydrateRoot and before the async code/data has loaded in to hydrate the target. However, this didn't really work out because code inside event handlers are expected to be able to synchronously read the state of the world at the time they're invoked. If we replay discrete events later, the mutable state around them like selection or form state etc. may have changed. This limitation doesn't apply to Client Actions: - They're expected to be async functions that themselves work asynchronously. They're conceptually also in the "navigation" events that happen after the "submit" events so they're already not synchronously even before the first `await`. - They're expected to operate mostly on the FormData as input which we can snapshot at the time of the event. This PR adds a bit of inline script to the Fizz runtime (or external runtime) to track any early submit events on the page - but only if the action URL is our placeholder `javascript:` URL. We track a queue of these on `document.$$reactFormReplay`. Then we replay them in order as they get hydrated and we get a handle on the Client Action function. I add the runtime to the `bootstrapScripts` phase in Fizz which is really technically a little too late, because on a large page, it might take a while to get to that script even if you have displayed the form. However, that's also true for external runtime. So there's a very short window we might miss an event but it's good enough and better than risking blocking display on this script. The main thing that makes the replaying difficult to reason about is that we can have multiple instance of React using this same queue. This would be very usual but you could have two different Reacts SSR:ing different parts of the tree and using around the same version. We don't have any coordinating ids for this. We could stash something on the form perhaps but given our current structure it's more difficult to get to the form instance in the commit phase and a naive solution wouldn't preserve ordering between forms. This solution isn't 100% guaranteed to preserve ordering between different React instances neither but should be in order within one instance which is the common case. The hard part is that we don't know what instance something will belong to until it hydrates. So to solve that I keep everything in the original queue while we wait, so that ordering is preserved until we know which instance it'll go into. I ended up doing a bunch of clever tricks to make this work. These could use a lot more tests than I have right now. Another thing that's tricky is that you can update the action before it's replayed but we actually want to invoke the old action if that happens. So we have to extract it even if we can't invoke it right now just so we get the one that was there during hydration. |
||
|
|
80cf4a099e |
Update Closure Compiler (#26205)
I need it for https://github.com/facebook/react/pull/26187. We need to specify specifically the output mode `ECMASCRIPT5_STRICT` to remove `const` from the Fizz runtime. |
||
|
|
6396b66411 |
Model Float on Hoistables semantics (#26106)
## Hoistables In the original implementation of Float, all hoisted elements were treated like Resources. They had deduplication semantics and hydrated based on a key. This made certain kinds of hoists very challenging such as sequences of meta tags for `og:image:...` metadata. The reason is each tag along is not dedupable based on only it's intrinsic properties. two identical tags may need to be included and hoisted together with preceding meta tags that describe a semantic object with a linear set of html nodes. It was clear that the concept of Browser Resources (stylesheets / scripts / preloads) did not extend universally to all hositable tags (title, meta, other links, etc...) Additionally while Resources benefit from deduping they suffer an inability to update because while we may have multiple rendered elements that refer to a single Resource it isn't unambiguous which element owns the props on the underlying resource. We could try merging props, but that is still really hard to reason about for authors. Instead we restrict Resource semantics to freezing the props at the time the Resource is first constructed and warn if you attempt to render the same Resource with different props via another rendered element or by updating an existing element for that Resource. This lack of updating restriction is however way more extreme than necessary for instances that get hoisted but otherwise do not dedupe; where there is a well defined DOM instance for each rendered element. We should be able to update props on these instances. Hoistable is a generalization of what Float tries to model for hoisting. Instead of assuming every hoistable element is a Resource we now have two distinct categories, hoistable elements and hoistable resources. As one might guess the former has semantics that match regular Host Components except the placement of the node is usually in the <head>. The latter continues to behave how the original implementation of HostResource behaved with the first iteration of Float ### Hoistable Element On the server hoistable elements render just like regular tags except the output is stored in special queues that can be emitted in the stream earlier than they otherwise would be if rendered in place. This also allow for instance the ability to render a hoistable before even rendering the <html> tag because the queues for hoistable elements won't flush until after we have flushed the preamble (`<DOCTYPE html><html><head>`). On the client, hoistable elements largely operate like HostComponents. The most notable difference is in the hydration strategy. If we are hydrating and encounter a hoistable element we will look for all tags in the document that could potentially be a match and we check whether the attributes match the props for this particular instance. We also do this in the commit phase rather than the render phase. The reason hydration can be done for HostComponents in render is the instance will be removed from the document if hydration fails so mutating it in render is safe. For hoistables the nodes are not in a hydration boundary (Root or SuspenseBoundary at time of writing) and thus if hydration fails and we may have an instance marked as bound to some Fiber when that Fiber never commits. Moving the hydration matching to commit ensures we will always succeed in pairing the hoisted DOM instance with a Fiber that has committed. ### Hoistable Resource On the server and client the semantics of Resources are largely the same they just don't apply to title, meta, and most link tags anymore. Resources hoist and dedupe via an `href` key and are ref counted. In a future update we will add a garbage collector so we can clean up Resources that no longer have any references ## `<style>` support In earlier implementations there was no support for <style> tags. This PR adds support for treating `<style href="..." precedence="...">...</style>` as a Resource analagous to `<link rel="stylesheet" href="..." precedence="..." />` It may seem odd at first to require an href to get Resource semantics for a style tag. The rationale is that these are for inlining of actual external stylesheets as an optimization and for URI like scoping of inline styles for css-in-js libraries. The href indicates that the key space for `<style>` and `<link rel="stylesheet" />` Resources is shared. and the precedence is there to allow for interleaving of both kinds of Style resources. This is an advanced feature that we do not expect most app developers to use directly but will be quite handy for various styling libraries and for folks who want to inline as much as possible once Fizz supports this feature. ## refactor notes * HostResource Fiber type is renamed HostHoistable to reflect the generalization of the concept * The Resource object representation is modified to reduce hidden class checks and to use less memory overall * The thing that distinguishes a resource from an element is whether the Fiber has a memoizedState. If it does, it will use resource semantics, otherwise element semantics * The time complexity of matching hositable elements for hydration should be improved |
||
|
|
0b974418c9 |
[Fizz] Fork Fizz instruction set for inline script and external runtime (#25862)
~~[Fizz] Duplicate completeBoundaryWithStyles to not reference globals~~ ## Summary Follow-up / cleanup PR to #25437 - `completeBoundaryWithStylesInlineLocals` is used by the Fizz external runtime, which bundles together all Fizz instruction functions (and is able to reference / rename `completeBoundary` and `resourceMap` as locals). - `completeBoundaryWithStylesInlineGlobals` is used by the Fizz inline script writer, which sends Fizz instruction functions on an as-needed basis. This version needs to reference `completeBoundary($RC)` and `resourceMap($RM)` as globals. Ideally, Closure would take care of inlining a shared implementation, but I couldn't figure out a zero-overhead inline due to lack of an `@inline` compiler directive. It seems that Closure thinks that a shared `completeBoundaryWithStyles` is too large and will always keep it as a separate function. I've also tried currying / writing a higher order function (`getCompleteBoundaryWithStyles`) with no luck ## How did you test this change? - generated Fizz inline instructions should be unchanged - bundle size for unstable_external_runtime should be slightly smaller (due to lack of globals) - `ReactDOMFizzServer-test.js` and `ReactDOMFloat-test.js` should be unaffected |
||
|
|
4494f2a86f |
[Float] add support for scripts and other enhancements (#25480)
* float enhance!!! Support preinit as script Support resources from async scripts Support saving the precedence place when rendering the shell There was a significant change to the flushing order of resources which follows the general principal of... 1. stuff that blocks display 2. stuff that we know will be used 3. stuff that was explicitly preloaded As a consequence if you preinit a style now it won't automatically flush in the shell unless you actually depend on it in your tree. To avoid races with precedence order we now emit a tag that saves the place amongst the precedence hierarchy so late insertions still end up where they were intended There is also a novel hydration pathway for certain tags. If you render an async script with an onLoad or onError it will always treat it like an insertion rather than a hydration. * restore preinit style flushing behavior and nits |
||
|
|
0eaca37565 |
Add script to generate inline Fizz runtime (#25481)
* Move Fizz inline instructions to unified module Instead of a separate module per instruction, this exports all of them from a unified module. In the next step, I'll add a script to generate this new module. * Add script to generate inline Fizz runtime This adds a script to generate the inline Fizz runtime. Previously, the runtime source was in an inline comment, and a compiled version of the instructions were hardcoded as strings into the Fizz implementation, where they are injected into the HTML stream. I've moved the source for the instructions to a regular JavaScript module. A script compiles the instructions with Closure, then generates another module that exports the compiled instructions as strings. Then the Fizz runtime imports the instructions from the generated module. To build the instructions, run: yarn generate-inline-fizz-runtime In the next step, I'll add a CI check to verify that the generated files are up to date. * Check in CI if generated Fizz runtime is in sync The generated Fizz runtime is checked into source. In CI, we'll ensure it stays in sync by running the script and confirming nothing changed. |