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

[Fiber] Transfer _debugInfo from Arrays, Lazy, Thenables and Elements to the inner Fibers. #28286

Merged
merged 3 commits into from
Feb 12, 2024

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Feb 9, 2024

That way we can use it for debug information like component stacks and DevTools. I used an extra stack argument in Child Fiber to track this as it's flowing down since it's not just elements where we have this info readily available but parent arrays and lazy can merge this into the Fiber too. It's not great that this is a dev-only argument and I could track it globally but seems more likely to make mistakes.

It is possible for the same debug info to appear for multiple child fibers like when it's attached to a fragment or a lazy that resolves to a fragment at the root. The object identity could be used in these scenarios to infer if that's really one server component that's a parent of all children or if each child has a server component with the same name.

This is effectively a public API because you can use it to stash information on Promises from a third-party service - not just Server Components. I started outline the types for this for some things I was planning to add but it's not final.

I was also planning on storing it from use(thenable) for when you suspend on a Promise. However, I realized that there's no Hook instance for those to stash it on. So it might need a separate data structure to stash the previous pass over of use() that resets each render.

No tests yet since I didn't want to test internals but it'll be covered once we have debugging features like component stacks.

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Feb 9, 2024
@react-sizebot
Copy link

react-sizebot commented Feb 9, 2024

Comparing: 629541b...83d3c62

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 = 176.82 kB 176.79 kB = 55.12 kB 55.11 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 178.81 kB 178.79 kB = 55.70 kB 55.69 kB
facebook-www/ReactDOM-prod.classic.js = 593.39 kB 593.33 kB = 104.69 kB 104.69 kB
facebook-www/ReactDOM-prod.modern.js = 577.17 kB 577.11 kB = 101.78 kB 101.78 kB
test_utils/ReactAllWarnings.js Deleted 67.02 kB 0.00 kB Deleted 16.42 kB 0.00 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-dev.js +0.45% 939.16 kB 943.41 kB +0.25% 188.19 kB 188.66 kB
facebook-www/ReactTestRenderer-dev.modern.js +0.45% 949.10 kB 953.35 kB +0.22% 189.86 kB 190.28 kB
facebook-www/ReactTestRenderer-dev.classic.js +0.45% 949.10 kB 953.35 kB +0.22% 189.86 kB 190.28 kB
react-native/implementations/ReactFabric-dev.js +0.44% 983.33 kB 987.62 kB +0.22% 198.48 kB 198.91 kB
react-native/implementations/ReactNativeRenderer-dev.js +0.43% 999.61 kB 1,003.91 kB +0.22% 202.75 kB 203.19 kB
react-native/implementations/ReactFabric-dev.fb.js +0.43% 1,005.11 kB 1,009.40 kB +0.22% 202.16 kB 202.61 kB
react-native/implementations/ReactNativeRenderer-dev.fb.js +0.42% 1,019.72 kB 1,024.01 kB +0.22% 206.02 kB 206.46 kB
facebook-www/ReactART-dev.modern.js +0.41% 1,052.33 kB 1,056.63 kB +0.21% 207.30 kB 207.73 kB
facebook-www/ReactART-dev.classic.js +0.40% 1,064.93 kB 1,069.23 kB +0.21% 209.70 kB 210.15 kB
oss-stable-semver/react-test-renderer/umd/react-test-renderer.development.js +0.31% 867.59 kB 870.26 kB +0.20% 183.85 kB 184.22 kB
oss-stable/react-test-renderer/umd/react-test-renderer.development.js +0.31% 867.61 kB 870.29 kB +0.20% 183.88 kB 184.25 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js +0.31% 828.61 kB 831.17 kB +0.20% 181.96 kB 182.33 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js +0.31% 828.64 kB 831.19 kB +0.20% 181.99 kB 182.36 kB
oss-experimental/react-test-renderer/umd/react-test-renderer.development.js +0.31% 868.91 kB 871.59 kB +0.20% 184.12 kB 184.49 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js +0.31% 829.90 kB 832.45 kB +0.20% 182.26 kB 182.62 kB
oss-stable-semver/react-art/cjs/react-art.development.js +0.31% 848.07 kB 850.66 kB +0.20% 185.70 kB 186.08 kB
oss-stable/react-art/cjs/react-art.development.js +0.31% 848.09 kB 850.69 kB +0.21% 185.73 kB 186.11 kB
oss-experimental/react-art/cjs/react-art.development.js +0.30% 860.28 kB 862.87 kB +0.20% 188.11 kB 188.49 kB
oss-stable-semver/react-art/umd/react-art.development.js +0.28% 964.66 kB 967.38 kB +0.19% 204.81 kB 205.19 kB
oss-stable/react-art/umd/react-art.development.js +0.28% 964.69 kB 967.41 kB +0.19% 204.83 kB 205.22 kB
oss-experimental/react-art/umd/react-art.development.js +0.28% 977.48 kB 980.20 kB +0.18% 207.30 kB 207.68 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.development.js +0.27% 948.26 kB 950.86 kB +0.18% 204.25 kB 204.62 kB
oss-stable/react-reconciler/cjs/react-reconciler.development.js +0.27% 948.29 kB 950.88 kB +0.18% 204.28 kB 204.66 kB
oss-experimental/react-reconciler/cjs/react-reconciler.development.js +0.27% 960.47 kB 963.07 kB +0.18% 206.65 kB 207.02 kB
facebook-www/ReactDOM-dev.modern.js +0.26% 1,636.78 kB 1,641.08 kB +0.13% 326.12 kB 326.55 kB
facebook-www/ReactDOMTesting-dev.modern.js +0.26% 1,658.31 kB 1,662.60 kB +0.13% 330.70 kB 331.13 kB
facebook-www/ReactDOM-dev.classic.js +0.26% 1,668.37 kB 1,672.67 kB +0.13% 331.81 kB 332.24 kB
facebook-www/ReactDOMTesting-dev.classic.js +0.25% 1,689.95 kB 1,694.25 kB +0.13% 336.38 kB 336.81 kB
test_utils/ReactAllWarnings.js Deleted 67.02 kB 0.00 kB Deleted 16.42 kB 0.00 kB

Generated by 🚫 dangerJS against 83d3c62

@sebmarkbage
Copy link
Collaborator Author

This doesn't track information if the Lazy resolves to null or if an Array has 0 children. In that scenario we'd lose out on being able to show something in the DevTools for this instance. We'd also lose out on being able to show that something suspended and then rendered null. We could potentially inject a fake empty fragment fiber to hold this information.

+stack?: string,
};

export type ReactDebugInfo = Array<ReactComponentInfo | ReactAsyncInfo>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

The plurality of Info switching when talking about the generic vs inner types is a little confusing. maybe ReactDebugEntries or ReactDebugList?

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 suspect there's a possibility that the inner type moves inwards into an inner set with an outer object that represents an instance of it. So this prepares to not have to rename the outer one.

Comment on lines +70 to +73
// If we have two debugInfo, we need to create a new one. This makes the array no longer
// live so we'll miss any future updates if we received more so ideally we should always
// do this after both have fully resolved/unsuspended.
return outer.concat(inner);
Copy link
Collaborator

Choose a reason for hiding this comment

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

should we just let the debug info optionally be a tuple of debug infos that get flattened wherever they get turned into stacks? then we can write into each array indefinitely?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yea I considered that but it's also annoying given that this will almost never happen. Since the Flight renderer will have a single row for (almost?) all these cases.

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 is perhaps in conflict with my other point that DebugInfo might become per-instance objects of some sorts too.

Merge any debug info we got from intermediate objects until we create or
update a fiber that can hold it.
@sebmarkbage sebmarkbage merged commit 3f93ca1 into facebook:main Feb 12, 2024
36 checks passed
github-actions bot pushed a commit that referenced this pull request Feb 12, 2024
…ts to the inner Fibers. (#28286)

That way we can use it for debug information like component stacks and
DevTools. I used an extra stack argument in Child Fiber to track this as
it's flowing down since it's not just elements where we have this info
readily available but parent arrays and lazy can merge this into the
Fiber too. It's not great that this is a dev-only argument and I could
track it globally but seems more likely to make mistakes.

It is possible for the same debug info to appear for multiple child
fibers like when it's attached to a fragment or a lazy that resolves to
a fragment at the root. The object identity could be used in these
scenarios to infer if that's really one server component that's a parent
of all children or if each child has a server component with the same
name.

This is effectively a public API because you can use it to stash
information on Promises from a third-party service - not just Server
Components. I started outline the types for this for some things I was
planning to add but it's not final.

I was also planning on storing it from `use(thenable)` for when you
suspend on a Promise. However, I realized that there's no Hook instance
for those to stash it on. So it might need a separate data structure to
stash the previous pass over of `use()` that resets each render.

No tests yet since I didn't want to test internals but it'll be covered
once we have debugging features like component stacks.

DiffTrain build for [3f93ca1](3f93ca1)
sebmarkbage added a commit that referenced this pull request Feb 12, 2024
Alternative to #28295.

Instead of stashing all of the Usables eagerly, we can extract them by
replaying the render when we need them like we do with any other hook.
We already had an implementation of `use()` but it wasn't quite
complete.

These can also include further DebugInfo on them such as what Server
Component rendered the Promise or async debug info. This is nice just to
see which use() calls were made in the side-panel but it can also be
used to gather everything that might have suspended.

Together with #28286 we cover the
case when a Promise was used a child and if it was unwrapped with use().
Notably we don't cover a Promise that was thrown (although we do support
that in a Server Component which maybe we shouldn't). Throwing a Promise
isn't officially supported though and that use case should move to the
use() Hook.

The pattern of conditionally suspending based on cache also isn't really
supported with the use() pattern. You should always call use() if you
previously called use() with the same input. This also ensures that we
can track what might have suspended rather than what actually did.

One limitation of this strategy is that it's hard to find all the places
something might suspend in a tree without rerendering all the fibers
again. So we might need to still add something to the tree to indicate
which Fibers may have further debug info / thenables.
huozhi added a commit to vercel/next.js that referenced this pull request Feb 23, 2024
### 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
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…ts to the inner Fibers. (facebook#28286)

That way we can use it for debug information like component stacks and
DevTools. I used an extra stack argument in Child Fiber to track this as
it's flowing down since it's not just elements where we have this info
readily available but parent arrays and lazy can merge this into the
Fiber too. It's not great that this is a dev-only argument and I could
track it globally but seems more likely to make mistakes.

It is possible for the same debug info to appear for multiple child
fibers like when it's attached to a fragment or a lazy that resolves to
a fragment at the root. The object identity could be used in these
scenarios to infer if that's really one server component that's a parent
of all children or if each child has a server component with the same
name.

This is effectively a public API because you can use it to stash
information on Promises from a third-party service - not just Server
Components. I started outline the types for this for some things I was
planning to add but it's not final.

I was also planning on storing it from `use(thenable)` for when you
suspend on a Promise. However, I realized that there's no Hook instance
for those to stash it on. So it might need a separate data structure to
stash the previous pass over of `use()` that resets each render.

No tests yet since I didn't want to test internals but it'll be covered
once we have debugging features like component stacks.
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
Alternative to facebook#28295.

Instead of stashing all of the Usables eagerly, we can extract them by
replaying the render when we need them like we do with any other hook.
We already had an implementation of `use()` but it wasn't quite
complete.

These can also include further DebugInfo on them such as what Server
Component rendered the Promise or async debug info. This is nice just to
see which use() calls were made in the side-panel but it can also be
used to gather everything that might have suspended.

Together with facebook#28286 we cover the
case when a Promise was used a child and if it was unwrapped with use().
Notably we don't cover a Promise that was thrown (although we do support
that in a Server Component which maybe we shouldn't). Throwing a Promise
isn't officially supported though and that use case should move to the
use() Hook.

The pattern of conditionally suspending based on cache also isn't really
supported with the use() pattern. You should always call use() if you
previously called use() with the same input. This also ensures that we
can track what might have suspended rather than what actually did.

One limitation of this strategy is that it's hard to find all the places
something might suspend in a tree without rerendering all the fibers
again. So we might need to still add something to the tree to indicate
which Fibers may have further debug info / thenables.
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
…ts to the inner Fibers. (#28286)

That way we can use it for debug information like component stacks and
DevTools. I used an extra stack argument in Child Fiber to track this as
it's flowing down since it's not just elements where we have this info
readily available but parent arrays and lazy can merge this into the
Fiber too. It's not great that this is a dev-only argument and I could
track it globally but seems more likely to make mistakes.

It is possible for the same debug info to appear for multiple child
fibers like when it's attached to a fragment or a lazy that resolves to
a fragment at the root. The object identity could be used in these
scenarios to infer if that's really one server component that's a parent
of all children or if each child has a server component with the same
name.

This is effectively a public API because you can use it to stash
information on Promises from a third-party service - not just Server
Components. I started outline the types for this for some things I was
planning to add but it's not final.

I was also planning on storing it from `use(thenable)` for when you
suspend on a Promise. However, I realized that there's no Hook instance
for those to stash it on. So it might need a separate data structure to
stash the previous pass over of `use()` that resets each render.

No tests yet since I didn't want to test internals but it'll be covered
once we have debugging features like component stacks.

DiffTrain build for commit 3f93ca1.
sebmarkbage added a commit that referenced this pull request Jun 11, 2024
Stacked on #29804.

Transferring of debugInfo was added in #28286. It represents the parent
stack between the current Fiber and into the next Fiber we're about to
create. I.e. Server Components in between. ~I don't love passing
DEV-only fields as arguments anyway since I don't trust closure to
remove unused arguments that way.~ EDIT: Actually it seems like closure
handled that just fine before which is why this is no change in prod.

Instead, I track it on the module scope. Notably with DON'T use
try/finally because if something throws we want to observe what it was
at the time we threw. Like the pattern we use many other places.

Now we can use this when we create the Throw Fiber to associate the
Server Components that were part of the parent stack before this error
threw. There by giving us the correct parent stacks at the location that
threw.
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