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

[Flight] add support for Lazy components in Flight server #24068

Merged
merged 3 commits into from
Mar 10, 2022

Conversation

gnoff
Copy link
Collaborator

@gnoff gnoff commented Mar 9, 2022

Lazy components suspend until resolved just like in Fizz. Add tests to confirm Lazy works with Shared Components and Client Component references.

Lazy components suspend until resolved just like in Fizz. Add tests to confirm Lazy works with Shared Components and Client Component references.
Comment on lines -455 to -458
case REACT_LAZY_TYPE:
throw new Error(
'React Lazy Components are not yet supported on the server.',
);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sebmarkbage

In testing I could not ever get this case to throw. the value at this point is a React.Element and not a symbol so it ends up throwing in attemptResolveElement instead. If I am missing a use case that did hit this path that needs to be covered by tests let me know

Copy link
Collaborator

@sebmarkbage sebmarkbage Mar 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can now use lazy in the position of React "nodes" (i.e. children). That's actually what Flight itself does on the client.

const element = React.lazy(async () => {
  return <Component prop="prop" />;
});
...
<div>{element}</div>

That should probably ideally work in Server Components too.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also technically, you could pass just the symbol. <Component foo={lazyComponent.$$typeof} />

Regardless, if we see it, it's probably a bug.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add support for it, thanks

Copy link
Collaborator Author

@gnoff gnoff Mar 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should we do if someone does this

const ElementDisguisedAsAComponent = React.lazy(async () => {
  return <Component prop={"original"} />;
});
...
<div><ElementDisguisedAsAComponent prop={"new"}</div>

feels like that should error in Dev and in prod we should render the actual element throwing away the "new" prop?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works the way I said it should in prod already and there is no conditional, we could add the conditional in dev only

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens in the client when you do this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the opposite is supported

const componentDisguisedAsElement = React.lazy(async () => {
  return Component;
});
...
<div>{componentDisguisedAsElement}</div>

it will render like you did i.e. empty props. I'm thinking this should also error in dev

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The componentDisguisedAsElement case seems like it should hit this case?

https://github.com/facebook/react/blob/main/packages/react-server/src/ReactFlightServer.js#L635

Regardless, it should behave as if there was no lazy around it.

Copy link
Collaborator Author

@gnoff gnoff Mar 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a good hueristic, I've updated tests and behavior to have these match. It'll be an error to use lazy elements as Components and vice versa just like without lazy

@gnoff gnoff requested a review from sebmarkbage March 9, 2022 22:24
@sizebot
Copy link

sizebot commented Mar 9, 2022

Comparing: 72a933d...a4329f4

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 = 130.94 kB 130.94 kB = 41.92 kB 41.92 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 136.01 kB 136.01 kB = 43.40 kB 43.40 kB
facebook-www/ReactDOM-prod.classic.js = 435.49 kB 435.49 kB = 79.78 kB 79.78 kB
facebook-www/ReactDOM-prod.modern.js = 421.91 kB 421.91 kB = 77.76 kB 77.76 kB
facebook-www/ReactDOMForked-prod.classic.js = 435.49 kB 435.49 kB = 79.78 kB 79.78 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
facebook-relay/flight/ReactFlightNativeRelayServer-prod.js +1.36% 20.87 kB 21.15 kB +0.52% 5.20 kB 5.23 kB
oss-experimental/react-server/cjs/react-server-flight.development.js +1.27% 37.99 kB 38.48 kB +0.77% 9.70 kB 9.78 kB
oss-stable-semver/react-server/cjs/react-server-flight.development.js +1.27% 37.99 kB 38.48 kB +0.77% 9.70 kB 9.78 kB
oss-stable/react-server/cjs/react-server-flight.development.js +1.27% 37.99 kB 38.48 kB +0.77% 9.70 kB 9.78 kB
facebook-relay/flight/ReactFlightNativeRelayServer-dev.js +1.14% 38.35 kB 38.79 kB +0.86% 9.78 kB 9.87 kB
facebook-www/ReactFlightDOMRelayServer-prod.classic.js +1.07% 26.42 kB 26.71 kB +0.40% 7.00 kB 7.03 kB
facebook-www/ReactFlightDOMRelayServer-prod.modern.js +1.07% 26.50 kB 26.78 kB +0.40% 7.04 kB 7.06 kB
facebook-www/ReactFlightDOMRelayServer-dev.classic.js +0.83% 52.79 kB 53.23 kB +0.68% 13.44 kB 13.53 kB
facebook-www/ReactFlightDOMRelayServer-dev.modern.js +0.83% 52.85 kB 53.29 kB +0.68% 13.46 kB 13.55 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.node.development.server.js +0.82% 59.25 kB 59.74 kB +0.52% 15.34 kB 15.42 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.node.development.server.js +0.82% 59.25 kB 59.74 kB +0.52% 15.34 kB 15.42 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.node.development.server.js +0.82% 59.31 kB 59.79 kB +0.52% 15.36 kB 15.44 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-writer.browser.development.server.js +0.81% 63.88 kB 64.40 kB +0.52% 16.04 kB 16.12 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-writer.browser.development.server.js +0.81% 63.88 kB 64.40 kB +0.52% 16.04 kB 16.12 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-writer.browser.development.server.js +0.81% 63.94 kB 64.46 kB +0.51% 16.06 kB 16.14 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js +0.80% 60.76 kB 61.25 kB +0.52% 15.83 kB 15.91 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js +0.80% 60.76 kB 61.25 kB +0.52% 15.83 kB 15.91 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js +0.80% 60.82 kB 61.30 kB +0.52% 15.85 kB 15.93 kB
oss-experimental/react-server/cjs/react-server-flight.production.min.js +0.61% 9.84 kB 9.90 kB +0.49% 3.69 kB 3.71 kB
oss-stable-semver/react-server/cjs/react-server-flight.production.min.js +0.61% 9.84 kB 9.90 kB +0.49% 3.69 kB 3.71 kB
oss-stable/react-server/cjs/react-server-flight.production.min.js +0.61% 9.84 kB 9.90 kB +0.49% 3.69 kB 3.71 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js +0.38% 15.87 kB 15.93 kB +0.25% 5.96 kB 5.97 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.node.production.min.server.js +0.38% 15.64 kB 15.70 kB +0.31% 5.80 kB 5.82 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.node.production.min.server.js +0.38% 15.64 kB 15.70 kB +0.31% 5.80 kB 5.82 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.node.production.min.server.js +0.38% 15.69 kB 15.75 kB +0.33% 5.83 kB 5.84 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js +0.38% 15.83 kB 15.89 kB +0.29% 5.93 kB 5.95 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js +0.38% 15.83 kB 15.89 kB +0.29% 5.93 kB 5.95 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-writer.browser.production.min.server.js +0.37% 16.02 kB 16.08 kB +0.39% 5.97 kB 6.00 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-writer.browser.production.min.server.js +0.37% 16.02 kB 16.08 kB +0.39% 5.97 kB 6.00 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-writer.browser.production.min.server.js +0.37% 16.06 kB 16.12 kB +0.38% 6.00 kB 6.02 kB

Generated by 🚫 dangerJS against a4329f4

React.Lazy can now return an element instead of a Component. This commit implements support for Lazy elements when server rendering.
// this can happen when a lazy component resolves to an element instead of
// a Component.
return attemptResolveElement(type.type, type.key, type.ref, type.props);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the solution is to just remove this and then let it fall through to the error case. You could also put custom error messages at the bottom of this case if you want to explain in plain words to the user what's wrong.

Semantically this is not right since it's dropping key, ref and props from the parent.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, the need for this went away when I added lazy resolution to resolveModelToJSON

adding lazying initialization toResolveModelToJson means we use attemptResolveElement's full logic on whatever the resolved type ends up being. This better aligns handling of misued Lazy types like a lazy element being used as a Component or a lazy Component being used as an element.
@gnoff gnoff requested a review from sebmarkbage March 10, 2022 19:03
@gnoff gnoff merged commit 581f0c4 into facebook:main Mar 10, 2022
@gnoff gnoff deleted the flight-support-lazy-components branch March 10, 2022 19:18
facebook-github-bot pushed a commit to facebook/react-native that referenced this pull request Mar 24, 2022
Summary:
This sync includes the following changes:
- **[3f8990898](facebook/react@3f8990898 )**: Fix test-build-devtools if build was generated by build-for-devtools ([#24088](facebook/react#24088)) //<Sebastian Silbermann>//
- **[577f2de46](facebook/react@577f2de46 )**: enableCacheElement flag ([#24131](facebook/react#24131)) //<David McCabe>//
- **[2e0d86d22](facebook/react@2e0d86d22 )**: Allow updating dehydrated root at lower priority without forcing client render ([#24082](facebook/react#24082)) //<Andrew Clark>//
- **[dbe9e732a](facebook/react@dbe9e732a )**: Avoid conditions where control flow is sufficient ([#24126](facebook/react#24126)) //<Sebastian Markbåge>//
- **[b075f9742](facebook/react@b075f9742 )**: Fix dispatch config type for skipBubbling ([#24109](facebook/react#24109)) //<Luna>//
- **[ef23a9ee8](facebook/react@ef23a9ee8 )**: Flag for text hydration mismatch ([#24107](facebook/react#24107)) //<salazarm>//
- **[0412f0c1a](facebook/react@0412f0c1a )**: add offscreen state node ([#24026](facebook/react#24026)) //<Luna Ruan>//
- **[43eb28339](facebook/react@43eb28339 )**: Add skipBubbling property to dispatch config ([#23366](facebook/react#23366)) //<Luna>//
- **[832e2987e](facebook/react@832e2987e )**: Revert accdientally merged PR ([#24081](facebook/react#24081)) //<Andrew Clark>//
- **[02b65fd8c](facebook/react@02b65fd8c )**: Allow updates at lower pri without forcing client render //<Andrew Clark>//
- **[83b941a51](facebook/react@83b941a51 )**: Add isRootDehydrated function //<Andrew Clark>//
- **[c8e4789e2](facebook/react@c8e4789e2 )**: Pass children to hydration root constructor //<Andrew Clark>//
- **[581f0c42e](facebook/react@581f0c42e )**: [Flight] add support for Lazy components in Flight server ([#24068](facebook/react#24068)) //<Josh Story>//
- **[72a933d28](facebook/react@72a933d28 )**: Gate legacy hidden ([#24047](facebook/react#24047)) //<Sebastian Markbåge>//
- **[b9de50d2f](facebook/react@b9de50d2f )**: Update test to reset modules instead of using private state ([#24055](facebook/react#24055)) //<Sebastian Markbåge>//
- **[c91892ec3](facebook/react@c91892ec3 )**: [Fizz] Don't flush empty segments ([#24054](facebook/react#24054)) //<Sebastian Markbåge>//
- **[d5f1b067c](facebook/react@d5f1b067c )**: [ServerContext] Flight support for ServerContext ([#23244](facebook/react#23244)) //<salazarm>//
- **[6edd55a3f](facebook/react@6edd55a3f )**: Gate unstable_expectedLoadTime on enableCPUSuspense ([#24038](facebook/react#24038)) //<Sebastian Markbåge>//
- **[57799b912](facebook/react@57799b912 )**: Add more feature flag checks ([#24037](facebook/react#24037)) //<Sebastian Markbåge>//
- **[e09518e5b](facebook/react@e09518e5b )**: [Fizz] write chunks to a buffer with no re-use ([#24034](facebook/react#24034)) //<Josh Story>//
- **[14c2be8da](facebook/react@14c2be8da )**: Rename Node SSR Callbacks to onShellReady/onAllReady and Other Fixes ([#24030](facebook/react#24030)) //<Sebastian Markbåge>//
- **[cb1e7b1c6](facebook/react@cb1e7b1c6 )**: Move onCompleteAll to .allReady Promise ([#24025](facebook/react#24025)) //<Sebastian Markbåge>//
- **[566285761](facebook/react@566285761 )**: [Fizz] Export debug function for FB ([#24024](facebook/react#24024)) //<salazarm>//
- **[05c283c3c](facebook/react@05c283c3c )**: Fabric HostComponent as EventEmitter: support add/removeEventListener (unstable only) ([#23386](facebook/react#23386)) //<Joshua Gross>//
- **[08644348b](facebook/react@08644348b )**: Added unit Tests in the ReactART, increasing the code coverage ([#23195](facebook/react#23195)) //<BIKI DAS>//
- **[feefe437f](facebook/react@feefe437f )**: Refactor Cache Code ([#23393](facebook/react#23393)) //<Luna Ruan>//

Changelog:
[General][Changed] - React Native sync for revisions 1780659...1159ff6

jest_e2e[run_all_tests]

Reviewed By: lunaleaps

Differential Revision: D34928167

fbshipit-source-id: 8c386f2be5871981d217ab9a514892ed88eafcfb
@gaearon gaearon mentioned this pull request Mar 29, 2022
zhengjitf pushed a commit to zhengjitf/react that referenced this pull request Apr 15, 2022
…4068)

* [Flight] add support for Lazy components in Flight server

Lazy components suspend until resolved just like in Fizz. Add tests to confirm Lazy works with Shared Components and Client Component references.

* Support Lazy elements

React.Lazy can now return an element instead of a Component. This commit implements support for Lazy elements when server rendering.

* add lazy initialization to resolveModelToJson

adding lazying initialization toResolveModelToJson means we use attemptResolveElement's full logic on whatever the resolved type ends up being. This better aligns handling of misued Lazy types like a lazy element being used as a Component or a lazy Component being used as an element.
Saadnajmi pushed a commit to Saadnajmi/react-native-macos that referenced this pull request Jan 15, 2023
Summary:
This sync includes the following changes:
- **[3f8990898](facebook/react@3f8990898 )**: Fix test-build-devtools if build was generated by build-for-devtools ([facebook#24088](facebook/react#24088)) //<Sebastian Silbermann>//
- **[577f2de46](facebook/react@577f2de46 )**: enableCacheElement flag ([facebook#24131](facebook/react#24131)) //<David McCabe>//
- **[2e0d86d22](facebook/react@2e0d86d22 )**: Allow updating dehydrated root at lower priority without forcing client render ([facebook#24082](facebook/react#24082)) //<Andrew Clark>//
- **[dbe9e732a](facebook/react@dbe9e732a )**: Avoid conditions where control flow is sufficient ([facebook#24126](facebook/react#24126)) //<Sebastian Markbåge>//
- **[b075f9742](facebook/react@b075f9742 )**: Fix dispatch config type for skipBubbling ([facebook#24109](facebook/react#24109)) //<Luna>//
- **[ef23a9ee8](facebook/react@ef23a9ee8 )**: Flag for text hydration mismatch ([facebook#24107](facebook/react#24107)) //<salazarm>//
- **[0412f0c1a](facebook/react@0412f0c1a )**: add offscreen state node ([facebook#24026](facebook/react#24026)) //<Luna Ruan>//
- **[43eb28339](facebook/react@43eb28339 )**: Add skipBubbling property to dispatch config ([facebook#23366](facebook/react#23366)) //<Luna>//
- **[832e2987e](facebook/react@832e2987e )**: Revert accdientally merged PR ([facebook#24081](facebook/react#24081)) //<Andrew Clark>//
- **[02b65fd8c](facebook/react@02b65fd8c )**: Allow updates at lower pri without forcing client render //<Andrew Clark>//
- **[83b941a51](facebook/react@83b941a51 )**: Add isRootDehydrated function //<Andrew Clark>//
- **[c8e4789e2](facebook/react@c8e4789e2 )**: Pass children to hydration root constructor //<Andrew Clark>//
- **[581f0c42e](facebook/react@581f0c42e )**: [Flight] add support for Lazy components in Flight server ([facebook#24068](facebook/react#24068)) //<Josh Story>//
- **[72a933d28](facebook/react@72a933d28 )**: Gate legacy hidden ([facebook#24047](facebook/react#24047)) //<Sebastian Markbåge>//
- **[b9de50d2f](facebook/react@b9de50d2f )**: Update test to reset modules instead of using private state ([facebook#24055](facebook/react#24055)) //<Sebastian Markbåge>//
- **[c91892ec3](facebook/react@c91892ec3 )**: [Fizz] Don't flush empty segments ([facebook#24054](facebook/react#24054)) //<Sebastian Markbåge>//
- **[d5f1b067c](facebook/react@d5f1b067c )**: [ServerContext] Flight support for ServerContext ([facebook#23244](facebook/react#23244)) //<salazarm>//
- **[6edd55a3f](facebook/react@6edd55a3f )**: Gate unstable_expectedLoadTime on enableCPUSuspense ([facebook#24038](facebook/react#24038)) //<Sebastian Markbåge>//
- **[57799b912](facebook/react@57799b912 )**: Add more feature flag checks ([facebook#24037](facebook/react#24037)) //<Sebastian Markbåge>//
- **[e09518e5b](facebook/react@e09518e5b )**: [Fizz] write chunks to a buffer with no re-use ([facebook#24034](facebook/react#24034)) //<Josh Story>//
- **[14c2be8da](facebook/react@14c2be8da )**: Rename Node SSR Callbacks to onShellReady/onAllReady and Other Fixes ([facebook#24030](facebook/react#24030)) //<Sebastian Markbåge>//
- **[cb1e7b1c6](facebook/react@cb1e7b1c6 )**: Move onCompleteAll to .allReady Promise ([facebook#24025](facebook/react#24025)) //<Sebastian Markbåge>//
- **[566285761](facebook/react@566285761 )**: [Fizz] Export debug function for FB ([facebook#24024](facebook/react#24024)) //<salazarm>//
- **[05c283c3c](facebook/react@05c283c3c )**: Fabric HostComponent as EventEmitter: support add/removeEventListener (unstable only) ([facebook#23386](facebook/react#23386)) //<Joshua Gross>//
- **[08644348b](facebook/react@08644348b )**: Added unit Tests in the ReactART, increasing the code coverage ([facebook#23195](facebook/react#23195)) //<BIKI DAS>//
- **[feefe437f](facebook/react@feefe437f )**: Refactor Cache Code ([facebook#23393](facebook/react#23393)) //<Luna Ruan>//

Changelog:
[General][Changed] - React Native sync for revisions 1780659...1159ff6

jest_e2e[run_all_tests]

Reviewed By: lunaleaps

Differential Revision: D34928167

fbshipit-source-id: 8c386f2be5871981d217ab9a514892ed88eafcfb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants