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

[Float][Fiber] Enable Float methods to be called outside of render #26557

Merged
merged 1 commit into from
Apr 20, 2023

Conversation

gnoff
Copy link
Collaborator

@gnoff gnoff commented Apr 5, 2023

Stacked on #26570

Previously we restricted Float methods to only being callable while rendering. This allowed us to make associations between calls and their position in the DOM tree, for instance hoisting preinitialized styles into a ShadowRoot or an iframe Document.

When considering how we are going to support Flight support in Float however it became clear that this restriction would lead to compromises on the implementation because the Flight client does not execute within the context of a client render. We want to be able to disaptch Float directives coming from Flight as soon as possible and this requires being able to call them outside of render.

this patch modifies Float so that its methods are callable anywhere. The main consequence of this change is Float will always use the Document the renderer script is running within as the HoistableRoot. This means if you preinit as style inside a component render targeting a ShadowRoot the style will load in the ownerDocument not the ShadowRoot. Practially speaking it means that preinit is not useful inside ShadowRoots and iframes.

This tradeoff was deemed acceptable because these methods are optimistic, not critical. Additionally, the other methods, preconntect, prefetchDNS, and preload, are not impacted because they already operated at the level of the ownerDocument and really only interface with the Network cache layer.

I added a couple additional fixes that were necessary for getting tests to pass that are worth considering separately.

The first commit improves the diff for waitForThrow so it compares strings if possible.

The second commit makes invokeGuardedCallback not use metaprogramming pattern and swallows any novel errors produced from trying to run the guarded callback. Swallowing may not be the best we can do but it at least protects React against rapid failure when something causes the dispatchEvent to throw.

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Apr 5, 2023
@react-sizebot
Copy link

react-sizebot commented Apr 5, 2023

Comparing: e5708b3...64257bf

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 164.67 kB 163.95 kB = 51.81 kB 51.67 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 168.43 kB 167.70 kB = 52.93 kB 52.81 kB
facebook-www/ReactDOM-prod.classic.js = 568.33 kB 566.07 kB = 100.65 kB 100.25 kB
facebook-www/ReactDOM-prod.modern.js = 552.06 kB 549.80 kB = 97.95 kB 97.57 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-persistent.development.js = 42.70 kB 42.60 kB = 9.63 kB 9.61 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-persistent.development.js = 42.70 kB 42.60 kB = 9.63 kB 9.61 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-persistent.development.js = 42.70 kB 42.60 kB = 9.63 kB 9.61 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer.development.js = 42.56 kB 42.47 kB = 9.61 kB 9.59 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer.development.js = 42.56 kB 42.47 kB = 9.61 kB 9.59 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer.development.js = 42.56 kB 42.47 kB = 9.61 kB 9.59 kB
facebook-www/ReactDOMTesting-dev.classic.js = 1,451.76 kB 1,448.52 kB = 313.42 kB 313.17 kB
facebook-www/ReactDOM-dev.classic.js = 1,433.36 kB 1,430.13 kB = 309.42 kB 308.72 kB
facebook-www/ReactDOMTesting-dev.modern.js = 1,423.87 kB 1,420.63 kB = 308.25 kB 307.53 kB
facebook-www/ReactDOM-dev.modern.js = 1,405.47 kB 1,402.24 kB = 303.82 kB 303.14 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.development.js = 1,296.72 kB 1,293.60 kB = 286.11 kB 285.43 kB
oss-experimental/react-dom/cjs/react-dom.development.js = 1,278.61 kB 1,275.51 kB = 281.78 kB 281.13 kB
oss-experimental/react-dom/umd/react-dom.development.js = 1,340.67 kB 1,337.40 kB = 284.54 kB 283.87 kB
oss-stable/react-dom/cjs/react-dom.development.js = 1,259.59 kB 1,256.49 kB = 278.13 kB 277.47 kB
oss-stable-semver/react-dom/cjs/react-dom.development.js = 1,259.57 kB 1,256.46 kB = 278.11 kB 277.44 kB
oss-stable/react-dom/umd/react-dom.development.js = 1,320.69 kB 1,317.42 kB = 281.01 kB 280.35 kB
oss-stable-semver/react-dom/umd/react-dom.development.js = 1,320.67 kB 1,317.40 kB = 280.99 kB 280.31 kB
facebook-www/ReactDOMTesting-prod.classic.js = 582.79 kB 580.88 kB = 104.28 kB 103.96 kB
facebook-www/ReactDOM-profiling.classic.js = 598.82 kB 596.55 kB = 105.15 kB 104.75 kB
facebook-www/ReactDOM-profiling.modern.js = 582.48 kB 580.22 kB = 102.44 kB 102.05 kB
facebook-www/ReactDOMTesting-prod.modern.js = 568.60 kB 566.34 kB = 102.07 kB 101.67 kB
facebook-www/ReactDOM-prod.classic.js = 568.33 kB 566.07 kB = 100.65 kB 100.25 kB
oss-experimental/react-dom/cjs/react-dom.profiling.min.js = 178.06 kB 177.33 kB = 55.35 kB 55.22 kB
oss-experimental/react-dom/umd/react-dom.profiling.min.js = 177.32 kB 176.60 kB = 55.63 kB 55.50 kB
facebook-www/ReactDOM-prod.modern.js = 552.06 kB 549.80 kB = 97.95 kB 97.57 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.min.js = 174.64 kB 173.92 kB = 55.26 kB 55.12 kB
oss-stable/react-dom/cjs/react-dom.profiling.min.js = 174.31 kB 173.58 kB = 54.25 kB 54.12 kB
oss-stable-semver/react-dom/cjs/react-dom.profiling.min.js = 174.23 kB 173.51 kB = 54.23 kB 54.09 kB
oss-stable/react-dom/umd/react-dom.profiling.min.js = 173.57 kB 172.84 kB = 54.56 kB 54.41 kB
oss-stable-semver/react-dom/umd/react-dom.profiling.min.js = 173.50 kB 172.77 kB = 54.54 kB 54.39 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 168.43 kB 167.70 kB = 52.93 kB 52.81 kB
oss-experimental/react-dom/umd/react-dom.production.min.js = 168.34 kB 167.61 kB = 53.29 kB 53.15 kB
oss-stable/react-dom/cjs/react-dom.production.min.js = 164.67 kB 163.95 kB = 51.81 kB 51.67 kB
oss-stable-semver/react-dom/cjs/react-dom.production.min.js = 164.60 kB 163.88 kB = 51.78 kB 51.65 kB
oss-stable/react-dom/umd/react-dom.production.min.js = 164.59 kB 163.86 kB = 52.19 kB 52.06 kB
oss-stable-semver/react-dom/umd/react-dom.production.min.js = 164.51 kB 163.79 kB = 52.16 kB 52.03 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-persistent.production.min.js = 15.69 kB 15.61 kB = 4.69 kB 4.67 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-persistent.production.min.js = 15.69 kB 15.61 kB = 4.69 kB 4.67 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-persistent.production.min.js = 15.69 kB 15.61 kB = 4.69 kB 4.67 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer.production.min.js = 15.62 kB 15.55 kB = 4.67 kB 4.65 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer.production.min.js = 15.62 kB 15.55 kB = 4.67 kB 4.65 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer.production.min.js = 15.62 kB 15.55 kB = 4.67 kB 4.65 kB

Generated by 🚫 dangerJS against 64257bf

@gnoff gnoff force-pushed the float-anytime branch 4 times, most recently from e25fbfb to a74c2c5 Compare April 5, 2023 19:21
@gnoff gnoff marked this pull request as ready for review April 5, 2023 20:06
@gnoff gnoff requested review from acdlite and sebmarkbage April 5, 2023 20:07
@gnoff gnoff force-pushed the float-anytime branch 2 times, most recently from ed97c69 to b5934f2 Compare April 6, 2023 00:19
@gnoff gnoff force-pushed the float-anytime branch 2 times, most recently from 47fd6ca to a2b27c0 Compare April 10, 2023 22:06
@gnoff gnoff force-pushed the float-anytime branch 2 times, most recently from 6917df1 to 0bdcfff Compare April 11, 2023 17:32
Previously we restricted Float methods to only being callable while rendering. This allowed us to make associations between calls and their position in the DOM tree, for instance hoisting preinitialized styles into a ShadowRoot or an iframe Document.

When considering how we are going to support Flight support in Float however it became clear that this restriction would lead to compromises on the implementation because the Flight client does not execute within the context of a client render. We want to be able to disaptch Float directives coming from Flight as soon as possible and this requires being able to call them outside of render.

this patch modifies Float so that its methods are callable anywhere. The main consequence of this change is Float will always use the Document the renderer script is running within as the HoistableRoot. This means if you preinit as style inside a component render targeting a ShadowRoot the style will load in the ownerDocument not the ShadowRoot. Practially speaking it means that preinit is not useful inside ShadowRoots and iframes.

This tradeoff was deemed acceptable because these methods are optimistic, not critical. Additionally, the other methods, preconntect, prefetchDNS, and preload, are not impacted because they already operated at the level of the ownerDocument and really only interface with the Network cache layer.

When Float was first implemented the HostDispatcher was set and unset during each render. Now that we support dispatching globally the host extensions that did this Dispatcher shuffling is now a noop. Since these methods exist in all HostConfigs and they noop everywhere now we shoudl just remove them.
@gnoff gnoff merged commit fdad813 into facebook:main Apr 20, 2023
@gnoff gnoff deleted the float-anytime branch April 20, 2023 21:40
kassens pushed a commit that referenced this pull request Apr 21, 2023
…26557)

Stacked on #26570 

Previously we restricted Float methods to only being callable while
rendering. This allowed us to make associations between calls and their
position in the DOM tree, for instance hoisting preinitialized styles
into a ShadowRoot or an iframe Document.

When considering how we are going to support Flight support in Float
however it became clear that this restriction would lead to compromises
on the implementation because the Flight client does not execute within
the context of a client render. We want to be able to disaptch Float
directives coming from Flight as soon as possible and this requires
being able to call them outside of render.

this patch modifies Float so that its methods are callable anywhere. The
main consequence of this change is Float will always use the Document
the renderer script is running within as the HoistableRoot. This means
if you preinit as style inside a component render targeting a ShadowRoot
the style will load in the ownerDocument not the ShadowRoot. Practially
speaking it means that preinit is not useful inside ShadowRoots and
iframes.

This tradeoff was deemed acceptable because these methods are
optimistic, not critical. Additionally, the other methods, preconntect,
prefetchDNS, and preload, are not impacted because they already operated
at the level of the ownerDocument and really only interface with the
Network cache layer.

I added a couple additional fixes that were necessary for getting tests
to pass that are worth considering separately.

The first commit improves the diff for `waitForThrow` so it compares
strings if possible.

The second commit makes invokeGuardedCallback not use metaprogramming
pattern and swallows any novel errors produced from trying to run the
guarded callback. Swallowing may not be the best we can do but it at
least protects React against rapid failure when something causes the
dispatchEvent to throw.
gnoff added a commit that referenced this pull request Apr 22, 2023
Stacked on #26557 

Supporting Float methods such as ReactDOM.preload() are challenging for
flight because it does not have an easy means to convey direct
executions in other environments. Because the flight wire format is a
JSON-like serialization that is expected to be rendered it currently
only describes renderable elements. We need a way to convey a function
invocation that gets run in the context of the client environment
whether that is Fizz or Fiber.

Fiber is somewhat straightforward because the HostDispatcher is always
active and we can just have the FlightClient dispatch the serialized
directive.

Fizz is much more challenging becaue the dispatcher is always scoped but
the specific request the dispatch belongs to is not readily available.
Environments that support AsyncLocalStorage (or in the future
AsyncContext) we will use this to be able to resolve directives in Fizz
to the appropriate Request. For other environments directives will be
elided. Right now this is pragmatic and non-breaking because all
directives are opportunistic and non-critical. If this changes in the
future we will need to reconsider how widespread support for async
context tracking is.

For Flight, if AsyncLocalStorage is available Float methods can be
called before and after await points and be expected to work. If
AsyncLocalStorage is not available float methods called in the sync
phase of a component render will be captured but anything after an await
point will be a noop. If a float call is dropped in this manner a DEV
warning should help you realize your code may need to be modified.

This PR also introduces a way for resources (Fizz) and hints (Flight) to
flush even if there is not active task being worked on. This will help
when Float methods are called in between async points within a function
execution but the task is blocked on the entire function finishing.

This PR also introduces deduping of Hints in Flight using the same
resource keys used in Fizz. This will help shrink payload sizes when the
same hint is attempted to emit over and over again
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…acebook#26557)

Stacked on facebook#26570 

Previously we restricted Float methods to only being callable while
rendering. This allowed us to make associations between calls and their
position in the DOM tree, for instance hoisting preinitialized styles
into a ShadowRoot or an iframe Document.

When considering how we are going to support Flight support in Float
however it became clear that this restriction would lead to compromises
on the implementation because the Flight client does not execute within
the context of a client render. We want to be able to disaptch Float
directives coming from Flight as soon as possible and this requires
being able to call them outside of render.

this patch modifies Float so that its methods are callable anywhere. The
main consequence of this change is Float will always use the Document
the renderer script is running within as the HoistableRoot. This means
if you preinit as style inside a component render targeting a ShadowRoot
the style will load in the ownerDocument not the ShadowRoot. Practially
speaking it means that preinit is not useful inside ShadowRoots and
iframes.

This tradeoff was deemed acceptable because these methods are
optimistic, not critical. Additionally, the other methods, preconntect,
prefetchDNS, and preload, are not impacted because they already operated
at the level of the ownerDocument and really only interface with the
Network cache layer.

I added a couple additional fixes that were necessary for getting tests
to pass that are worth considering separately.

The first commit improves the diff for `waitForThrow` so it compares
strings if possible.

The second commit makes invokeGuardedCallback not use metaprogramming
pattern and swallows any novel errors produced from trying to run the
guarded callback. Swallowing may not be the best we can do but it at
least protects React against rapid failure when something causes the
dispatchEvent to throw.
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
Stacked on facebook#26557 

Supporting Float methods such as ReactDOM.preload() are challenging for
flight because it does not have an easy means to convey direct
executions in other environments. Because the flight wire format is a
JSON-like serialization that is expected to be rendered it currently
only describes renderable elements. We need a way to convey a function
invocation that gets run in the context of the client environment
whether that is Fizz or Fiber.

Fiber is somewhat straightforward because the HostDispatcher is always
active and we can just have the FlightClient dispatch the serialized
directive.

Fizz is much more challenging becaue the dispatcher is always scoped but
the specific request the dispatch belongs to is not readily available.
Environments that support AsyncLocalStorage (or in the future
AsyncContext) we will use this to be able to resolve directives in Fizz
to the appropriate Request. For other environments directives will be
elided. Right now this is pragmatic and non-breaking because all
directives are opportunistic and non-critical. If this changes in the
future we will need to reconsider how widespread support for async
context tracking is.

For Flight, if AsyncLocalStorage is available Float methods can be
called before and after await points and be expected to work. If
AsyncLocalStorage is not available float methods called in the sync
phase of a component render will be captured but anything after an await
point will be a noop. If a float call is dropped in this manner a DEV
warning should help you realize your code may need to be modified.

This PR also introduces a way for resources (Fizz) and hints (Flight) to
flush even if there is not active task being worked on. This will help
when Float methods are called in between async points within a function
execution but the task is blocked on the entire function finishing.

This PR also introduces deduping of Hints in Flight using the same
resource keys used in Fizz. This will help shrink payload sizes when the
same hint is attempted to emit over and over again
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
…26557)

Stacked on #26570

Previously we restricted Float methods to only being callable while
rendering. This allowed us to make associations between calls and their
position in the DOM tree, for instance hoisting preinitialized styles
into a ShadowRoot or an iframe Document.

When considering how we are going to support Flight support in Float
however it became clear that this restriction would lead to compromises
on the implementation because the Flight client does not execute within
the context of a client render. We want to be able to disaptch Float
directives coming from Flight as soon as possible and this requires
being able to call them outside of render.

this patch modifies Float so that its methods are callable anywhere. The
main consequence of this change is Float will always use the Document
the renderer script is running within as the HoistableRoot. This means
if you preinit as style inside a component render targeting a ShadowRoot
the style will load in the ownerDocument not the ShadowRoot. Practially
speaking it means that preinit is not useful inside ShadowRoots and
iframes.

This tradeoff was deemed acceptable because these methods are
optimistic, not critical. Additionally, the other methods, preconntect,
prefetchDNS, and preload, are not impacted because they already operated
at the level of the ownerDocument and really only interface with the
Network cache layer.

I added a couple additional fixes that were necessary for getting tests
to pass that are worth considering separately.

The first commit improves the diff for `waitForThrow` so it compares
strings if possible.

The second commit makes invokeGuardedCallback not use metaprogramming
pattern and swallows any novel errors produced from trying to run the
guarded callback. Swallowing may not be the best we can do but it at
least protects React against rapid failure when something causes the
dispatchEvent to throw.

DiffTrain build for commit fdad813.
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
Stacked on #26557

Supporting Float methods such as ReactDOM.preload() are challenging for
flight because it does not have an easy means to convey direct
executions in other environments. Because the flight wire format is a
JSON-like serialization that is expected to be rendered it currently
only describes renderable elements. We need a way to convey a function
invocation that gets run in the context of the client environment
whether that is Fizz or Fiber.

Fiber is somewhat straightforward because the HostDispatcher is always
active and we can just have the FlightClient dispatch the serialized
directive.

Fizz is much more challenging becaue the dispatcher is always scoped but
the specific request the dispatch belongs to is not readily available.
Environments that support AsyncLocalStorage (or in the future
AsyncContext) we will use this to be able to resolve directives in Fizz
to the appropriate Request. For other environments directives will be
elided. Right now this is pragmatic and non-breaking because all
directives are opportunistic and non-critical. If this changes in the
future we will need to reconsider how widespread support for async
context tracking is.

For Flight, if AsyncLocalStorage is available Float methods can be
called before and after await points and be expected to work. If
AsyncLocalStorage is not available float methods called in the sync
phase of a component render will be captured but anything after an await
point will be a noop. If a float call is dropped in this manner a DEV
warning should help you realize your code may need to be modified.

This PR also introduces a way for resources (Fizz) and hints (Flight) to
flush even if there is not active task being worked on. This will help
when Float methods are called in between async points within a function
execution but the task is blocked on the entire function finishing.

This PR also introduces deduping of Hints in Flight using the same
resource keys used in Fizz. This will help shrink payload sizes when the
same hint is attempted to emit over and over again

DiffTrain build for commit 36e4cbe.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants