-
Notifications
You must be signed in to change notification settings - Fork 47k
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
Suspend Thenable/Lazy if it's used in React.Children and unwrap #28284
Conversation
@@ -212,19 +212,19 @@ export function trackUsedThenable<T>( | |||
} | |||
}, | |||
); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@acdlite I believe that the end curly was not in the right position because if we hit this path with an uninitialized Flight object, we should be able to synchronously return the value without suspending once.
throw rejectedError; | ||
} | ||
// Check one more time in case the thenable resolved synchronously. | ||
switch (thenable.status) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that it's an issue per se, but why did this move into the outer block? Because the .then(noop)
triggers a lazy initializer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. #28284 (comment)
2e46108
to
adb0bef
Compare
Comparing: ba5e6a8...adb0bef Critical size changesIncludes critical production bundles, as well as any change greater than 2%:
Significant size changesIncludes any change greater than 0.2%: Expand to show
|
This pains me because `React.Children` is really already pseudo-deprecated. `React.Children` takes any children that `React.Node` takes. We now support Lazy and Thenable in this position elsewhere, but it errors in `React.Children`. This becomes an issue with async Server Components which can resolve into a Lazy and in the future Lazy will just become Thenables. Which causes this to error. There are a few different semantics we could have: 1) Error like we already do (#28280). `React.Children` is about introspecting children. It was always sketchy because you can't introspect inside an abstraction anyway. With Server Components we fold away the components so you can actually introspect inside of them kind of but what they do is an implementation detail and you should be able to turn it into a Client Component at any point. The type of an Element passing the boundary actually reduces to `React.Node`. 2) Suspend and unwrap the Node (this PR). If we assume that Children is called inside of render, then throwing a Promise if it's not already loaded or unwrapping would treat it as if it wasn't there. Just like if you rendered it in React. This lets you introspect what's inside which isn't really something you should be able to do. This isn't compatible with deprecating throwing-a-Promise and enable static compilation like `use()` does. We'd have to deprecate `React.Children` before doing that which we might anyway. 3) Wrap in a Fragment. If a Server Component was instead a Client Component, you couldn't introspect through it anyway. Another alternative might be to let it pass through but then it wouldn't be given a flat key. We could also wrap it in a Fragment that is keyed. That way you're always seeing an element. The issue with this solution is that it wouldn't see the key of the Server Component since that gets forwarded to the child that is yet to resolve. The nice thing about that strategy is it doesn't depend on throw-a-Promise but it might not be keyed correctly when things move. DiffTrain build for [9e7944f](9e7944f)
### React upstream changes - facebook/react#28333 - facebook/react#28334 - facebook/react#28378 - facebook/react#28377 - facebook/react#28376 - facebook/react#28338 - facebook/react#28331 - facebook/react#28336 - facebook/react#28320 - facebook/react#28317 - facebook/react#28375 - facebook/react#28367 - facebook/react#28380 - facebook/react#28368 - facebook/react#28343 - facebook/react#28355 - facebook/react#28374 - facebook/react#28362 - facebook/react#28344 - facebook/react#28339 - facebook/react#28353 - facebook/react#28346 - facebook/react#25790 - facebook/react#28352 - facebook/react#28326 - facebook/react#27688 - facebook/react#28329 - facebook/react#28332 - facebook/react#28340 - facebook/react#28327 - facebook/react#28325 - facebook/react#28324 - facebook/react#28309 - facebook/react#28310 - facebook/react#28307 - facebook/react#28306 - facebook/react#28315 - facebook/react#28318 - facebook/react#28226 - facebook/react#28308 - facebook/react#27563 - facebook/react#28297 - facebook/react#28286 - facebook/react#28284 - facebook/react#28275 - facebook/react#28145 - facebook/react#28301 - facebook/react#28224 - facebook/react#28152 - facebook/react#28296 - facebook/react#28294 - facebook/react#28279 - facebook/react#28273 - facebook/react#28269 - facebook/react#28376 - facebook/react#28338 - facebook/react#28331 - facebook/react#28336 - facebook/react#28320 - facebook/react#28317 - facebook/react#28375 - facebook/react#28367 - facebook/react#28380 - facebook/react#28368 - facebook/react#28343 - facebook/react#28355 - facebook/react#28374 - facebook/react#28362 - facebook/react#28344 - facebook/react#28339 - facebook/react#28353 - facebook/react#28346 - facebook/react#25790 - facebook/react#28352 - facebook/react#28326 - facebook/react#27688 - facebook/react#28329 - facebook/react#28332 - facebook/react#28340 - facebook/react#28327 - facebook/react#28325 - facebook/react#28324 - facebook/react#28309 - facebook/react#28310 - facebook/react#28307 - facebook/react#28306 - facebook/react#28315 - facebook/react#28318 - facebook/react#28226 - facebook/react#28308 - facebook/react#27563 - facebook/react#28297 - facebook/react#28286 - facebook/react#28284 - facebook/react#28275 - facebook/react#28145 - facebook/react#28301 - facebook/react#28224 - facebook/react#28152 - facebook/react#28296 - facebook/react#28294 - facebook/react#28279 - facebook/react#28273 - facebook/react#28269 Closes NEXT-2542 Disable ppr test for strict mode for now, @acdlite will check it and we'll sync again
…ap (facebook#28284)" This reverts commit 9e7944f.
…ap (facebook#28284)" This reverts commit 9e7944f.
…and unwrap (facebook#28284)"" This reverts commit 74b1848.
…book#28284) This pains me because `React.Children` is really already pseudo-deprecated. `React.Children` takes any children that `React.Node` takes. We now support Lazy and Thenable in this position elsewhere, but it errors in `React.Children`. This becomes an issue with async Server Components which can resolve into a Lazy and in the future Lazy will just become Thenables. Which causes this to error. There are a few different semantics we could have: 1) Error like we already do (facebook#28280). `React.Children` is about introspecting children. It was always sketchy because you can't introspect inside an abstraction anyway. With Server Components we fold away the components so you can actually introspect inside of them kind of but what they do is an implementation detail and you should be able to turn it into a Client Component at any point. The type of an Element passing the boundary actually reduces to `React.Node`. 2) Suspend and unwrap the Node (this PR). If we assume that Children is called inside of render, then throwing a Promise if it's not already loaded or unwrapping would treat it as if it wasn't there. Just like if you rendered it in React. This lets you introspect what's inside which isn't really something you should be able to do. This isn't compatible with deprecating throwing-a-Promise and enable static compilation like `use()` does. We'd have to deprecate `React.Children` before doing that which we might anyway. 3) Wrap in a Fragment. If a Server Component was instead a Client Component, you couldn't introspect through it anyway. Another alternative might be to let it pass through but then it wouldn't be given a flat key. We could also wrap it in a Fragment that is keyed. That way you're always seeing an element. The issue with this solution is that it wouldn't see the key of the Server Component since that gets forwarded to the child that is yet to resolve. The nice thing about that strategy is it doesn't depend on throw-a-Promise but it might not be keyed correctly when things move.
This pains me because `React.Children` is really already pseudo-deprecated. `React.Children` takes any children that `React.Node` takes. We now support Lazy and Thenable in this position elsewhere, but it errors in `React.Children`. This becomes an issue with async Server Components which can resolve into a Lazy and in the future Lazy will just become Thenables. Which causes this to error. There are a few different semantics we could have: 1) Error like we already do (#28280). `React.Children` is about introspecting children. It was always sketchy because you can't introspect inside an abstraction anyway. With Server Components we fold away the components so you can actually introspect inside of them kind of but what they do is an implementation detail and you should be able to turn it into a Client Component at any point. The type of an Element passing the boundary actually reduces to `React.Node`. 2) Suspend and unwrap the Node (this PR). If we assume that Children is called inside of render, then throwing a Promise if it's not already loaded or unwrapping would treat it as if it wasn't there. Just like if you rendered it in React. This lets you introspect what's inside which isn't really something you should be able to do. This isn't compatible with deprecating throwing-a-Promise and enable static compilation like `use()` does. We'd have to deprecate `React.Children` before doing that which we might anyway. 3) Wrap in a Fragment. If a Server Component was instead a Client Component, you couldn't introspect through it anyway. Another alternative might be to let it pass through but then it wouldn't be given a flat key. We could also wrap it in a Fragment that is keyed. That way you're always seeing an element. The issue with this solution is that it wouldn't see the key of the Server Component since that gets forwarded to the child that is yet to resolve. The nice thing about that strategy is it doesn't depend on throw-a-Promise but it might not be keyed correctly when things move. DiffTrain build for commit 9e7944f.
This pains me because
React.Children
is really already pseudo-deprecated.React.Children
takes any children thatReact.Node
takes. We now support Lazy and Thenable in this position elsewhere, but it errors inReact.Children
.This becomes an issue with async Server Components which can resolve into a Lazy and in the future Lazy will just become Thenables. Which causes this to error.
There are a few different semantics we could have:
React.Children
is about introspecting children. It was always sketchy because you can't introspect inside an abstraction anyway. With Server Components we fold away the components so you can actually introspect inside of them kind of but what they do is an implementation detail and you should be able to turn it into a Client Component at any point. The type of an Element passing the boundary actually reduces toReact.Node
.use()
does. We'd have to deprecateReact.Children
before doing that which we might anyway.