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

Track Stack of JSX Calls #29032

Merged
merged 2 commits into from
May 9, 2024
Merged

Track Stack of JSX Calls #29032

merged 2 commits into from
May 9, 2024

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented May 9, 2024

This is the first step to experimenting with a new type of stack traces behind the enableOwnerStacks flag - in DEV only.

The idea is to generate stacks that are more like if the JSX was a direct call even though it's actually a lazy call. Not only can you see which exact JSX call line number generated the erroring component but if that's inside an abstraction function, which function called that function and if it's a component, which component generated that component. For this to make sense it really need to be the "owner" stack rather than the parent stack like we do for other component stacks. On one hand it has more precise information but on the other hand it also loses context. For most types of problems the owner stack is the most useful though since it tells you which component rendered this component.

The problem with the platform in its current state is that there's two ways to deal with stacks:

  1. new Error().stack
  2. console.createTask()

The nice thing about new Error().stack is that we can extract the frames and piece them together in whatever way we want. That is great for constructing custom UIs like error dialogs. Unfortunately, we can't take custom stacks and set them in the native UIs like Chrome DevTools.

The nice thing about console.createTask() is that the resulting stacks are natively integrated into the Chrome DevTools in the console and the breakpoint debugger. They also automatically follow source mapping and ignoreLists. The downside is that there's no way to extract the async stack outside the native UI itself so this information cannot be used for custom UIs like errors dialogs. It also means we can't collect this on the server and then pass it to the client for server components.

The solution here is that we use both techniques and collect both an Error object and a Task object for every JSX call.

The main concern about this approach is the performance so that's the main thing to test. It's certainly too slow for production but it might also be too slow even for DEV.

This first PR doesn't actually use the stacks yet. It just collects them as the first step. The next step is to start utilizing this information in error printing etc.

For RSC we pass the stack along across over the wire. This can be concatenated on the client following the owner path to create an owner stack leading back into the server. We'll later use this information to restore fake frames on the client for native integration. Since this information quickly gets pretty heavy if we include all frames, we strip out the top frame. We also strip out everything below the functions that call into user space in the Flight runtime. To do this we need to figure out the frames that represents calling out into user space. The resulting stack is typically just the one frame inside the owner component's JSX callsite. I also eagerly strip out things we expect to be ignoreList:ed anyway - such as node_modules and Node.js internals.

@sebmarkbage sebmarkbage requested review from gnoff and acdlite May 9, 2024 02:54
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels May 9, 2024
@sebmarkbage sebmarkbage force-pushed the ownerstacks branch 3 times, most recently from 51e401d to d64c7fe Compare May 9, 2024 03:03
// We use this to trick the filtering of Flight to exclude this frame.
Object.defineProperty(wrapper, 'name', {
value: '(<anonymous>)',
});
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This hack wrapper adds another stack frame which I'd rather not include in the assertions in the tests.

I can use this hack in the first phase because so far I'm just asserting on the RSC protocol but this won't work with the client where we won't filter it in React but rather rely on the UI to filter this out.

@react-sizebot
Copy link

react-sizebot commented May 9, 2024

Comparing: 04b0588...80f37ab

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.js = 6.66 kB 6.66 kB = 1.82 kB 1.82 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 494.06 kB 494.06 kB = 88.22 kB 88.22 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.67 kB 6.67 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 498.86 kB 498.86 kB = 88.93 kB 88.93 kB
facebook-www/ReactDOM-prod.classic.js = 591.22 kB 591.22 kB = 103.96 kB 103.96 kB
facebook-www/ReactDOM-prod.modern.js = 567.44 kB 567.44 kB = 100.36 kB 100.36 kB
oss-experimental/react-server/cjs/react-server-flight.development.js +3.42% 113.66 kB 117.55 kB +3.87% 24.92 kB 25.89 kB
oss-experimental/react/cjs/react-jsx-dev-runtime.development.js +2.82% 41.41 kB 42.58 kB +1.62% 12.52 kB 12.72 kB
oss-experimental/react/cjs/react-jsx-runtime.development.js +2.74% 42.64 kB 43.81 kB +1.63% 12.86 kB 13.07 kB
oss-experimental/react/cjs/react-jsx-runtime.react-server.development.js +2.72% 43.05 kB 44.21 kB +1.62% 12.99 kB 13.20 kB
oss-experimental/react/cjs/react-jsx-dev-runtime.react-server.development.js +2.72% 43.05 kB 44.22 kB +1.62% 12.99 kB 13.20 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +2.38% 162.94 kB 166.83 kB +2.70% 35.74 kB 36.70 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js +2.33% 166.58 kB 170.47 kB +2.64% 36.39 kB 37.35 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js +2.30% 168.51 kB 172.39 kB +2.59% 36.92 kB 37.87 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +2.30% 169.03 kB 172.92 kB +2.59% 37.06 kB 38.02 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.development.js +2.30% 169.10 kB 172.99 kB +2.61% 36.72 kB 37.68 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +2.28% 170.44 kB 174.32 kB +2.56% 37.44 kB 38.40 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js +2.27% 171.39 kB 175.28 kB +2.57% 37.34 kB 38.30 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js +2.26% 171.75 kB 175.63 kB +2.55% 37.56 kB 38.52 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +2.23% 174.03 kB 177.92 kB +2.52% 38.18 kB 39.14 kB
test_utils/ReactAllWarnings.js Deleted 64.26 kB 0.00 kB Deleted 16.02 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
oss-experimental/react-server/cjs/react-server-flight.development.js +3.42% 113.66 kB 117.55 kB +3.87% 24.92 kB 25.89 kB
oss-experimental/react/cjs/react-jsx-dev-runtime.development.js +2.82% 41.41 kB 42.58 kB +1.62% 12.52 kB 12.72 kB
oss-experimental/react/cjs/react-jsx-runtime.development.js +2.74% 42.64 kB 43.81 kB +1.63% 12.86 kB 13.07 kB
oss-experimental/react/cjs/react-jsx-runtime.react-server.development.js +2.72% 43.05 kB 44.21 kB +1.62% 12.99 kB 13.20 kB
oss-experimental/react/cjs/react-jsx-dev-runtime.react-server.development.js +2.72% 43.05 kB 44.22 kB +1.62% 12.99 kB 13.20 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +2.38% 162.94 kB 166.83 kB +2.70% 35.74 kB 36.70 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js +2.33% 166.58 kB 170.47 kB +2.64% 36.39 kB 37.35 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js +2.30% 168.51 kB 172.39 kB +2.59% 36.92 kB 37.87 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +2.30% 169.03 kB 172.92 kB +2.59% 37.06 kB 38.02 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.development.js +2.30% 169.10 kB 172.99 kB +2.61% 36.72 kB 37.68 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +2.28% 170.44 kB 174.32 kB +2.56% 37.44 kB 38.40 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js +2.27% 171.39 kB 175.28 kB +2.57% 37.34 kB 38.30 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js +2.26% 171.75 kB 175.63 kB +2.55% 37.56 kB 38.52 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +2.23% 174.03 kB 177.92 kB +2.52% 38.18 kB 39.14 kB
oss-experimental/react/cjs/react.react-server.development.js +1.69% 74.63 kB 75.89 kB +1.01% 20.91 kB 21.12 kB
oss-experimental/react/cjs/react.development.js +1.33% 94.82 kB 96.09 kB +0.80% 25.96 kB 26.16 kB
oss-experimental/react-client/cjs/react-client-flight.development.js +0.68% 87.19 kB 87.78 kB +0.75% 19.50 kB 19.65 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +0.68% 87.20 kB 87.79 kB +0.76% 19.20 kB 19.35 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +0.68% 87.44 kB 88.03 kB +0.77% 19.27 kB 19.42 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js +0.65% 90.69 kB 91.28 kB +0.72% 20.24 kB 20.38 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +0.65% 91.20 kB 91.79 kB +0.72% 20.40 kB 20.55 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +0.62% 95.12 kB 95.71 kB +0.69% 21.30 kB 21.45 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.unbundled.development.js +0.61% 96.90 kB 97.49 kB +0.67% 21.87 kB 22.02 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +0.61% 96.93 kB 97.52 kB +0.67% 21.90 kB 22.04 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js +0.60% 98.33 kB 98.92 kB +0.66% 22.24 kB 22.39 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +0.60% 98.36 kB 98.95 kB +0.66% 22.28 kB 22.43 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js +0.59% 99.40 kB 99.99 kB +0.66% 22.44 kB 22.59 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +0.59% 99.43 kB 100.02 kB +0.65% 22.49 kB 22.63 kB
oss-stable-semver/react-server/cjs/react-server-flight.development.js +0.49% 87.32 kB 87.75 kB +0.50% 20.32 kB 20.42 kB
oss-stable/react-server/cjs/react-server-flight.development.js +0.49% 87.32 kB 87.75 kB +0.50% 20.32 kB 20.42 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +0.34% 124.64 kB 125.07 kB +0.34% 28.83 kB 28.93 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +0.34% 124.64 kB 125.07 kB +0.34% 28.83 kB 28.93 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js +0.34% 127.38 kB 127.81 kB +0.34% 29.37 kB 29.47 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js +0.34% 127.38 kB 127.81 kB +0.34% 29.37 kB 29.47 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +0.33% 129.83 kB 130.26 kB +0.30% 30.03 kB 30.12 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +0.33% 129.83 kB 130.26 kB +0.30% 30.03 kB 30.12 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js +0.33% 128.63 kB 129.05 kB +0.31% 29.66 kB 29.75 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js +0.33% 128.63 kB 129.05 kB +0.31% 29.66 kB 29.75 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.development.js +0.32% 130.80 kB 131.23 kB +0.32% 29.83 kB 29.93 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.development.js +0.32% 130.80 kB 131.23 kB +0.32% 29.83 kB 29.93 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +0.32% 130.56 kB 130.98 kB +0.32% 30.15 kB 30.25 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +0.32% 130.56 kB 130.98 kB +0.32% 30.15 kB 30.25 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js +0.32% 133.09 kB 133.52 kB +0.31% 30.41 kB 30.51 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js +0.32% 133.09 kB 133.52 kB +0.31% 30.41 kB 30.51 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js +0.32% 133.44 kB 133.87 kB +0.32% 30.64 kB 30.74 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js +0.32% 133.44 kB 133.87 kB +0.32% 30.64 kB 30.74 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +0.31% 135.73 kB 136.15 kB +0.30% 31.25 kB 31.34 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +0.31% 135.73 kB 136.15 kB +0.30% 31.25 kB 31.34 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js +0.21% 62.77 kB 62.90 kB +0.33% 15.16 kB 15.21 kB
oss-stable/react-client/cjs/react-client-flight.development.js +0.21% 62.77 kB 62.90 kB +0.33% 15.16 kB 15.21 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +0.21% 62.78 kB 62.91 kB +0.34% 14.87 kB 14.92 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +0.21% 62.78 kB 62.91 kB +0.34% 14.87 kB 14.92 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +0.21% 63.02 kB 63.15 kB +0.33% 14.93 kB 14.98 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +0.21% 63.02 kB 63.15 kB +0.33% 14.93 kB 14.98 kB
test_utils/ReactAllWarnings.js Deleted 64.26 kB 0.00 kB Deleted 16.02 kB 0.00 kB

Generated by 🚫 dangerJS against 80f37ab

@sebmarkbage sebmarkbage force-pushed the ownerstacks branch 4 times, most recently from 8103fb3 to 62dbf41 Compare May 9, 2024 04:01
We track the debug stack using an error. We don't eagerly access the stack
to allow it to be lazily generated from the internals. This will be used
for passing RSC stacks to the client as well as for user space display.

If available, we also track console.createTask separately. This will be
used for native stack traces in the native DevTools.
Copy link
Collaborator

@sophiebits sophiebits left a comment

Choose a reason for hiding this comment

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

fancy

// Extract the stack frame of the callIteratorInDEV function.
try {
(callIteratorInDEV: any)({next: null});
return '';
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: should this throw if we can't find the stack? seems like the caller won't do the right thing if this is empty string

(e.g. remove this return and add a throw after the catch)

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 think I need some protection if any of these yield some nonsense or can't be found. Throwing isn't great because it would break the app for non-essential debug information.

@sebmarkbage sebmarkbage merged commit 151cce3 into facebook:main May 9, 2024
38 checks passed
github-actions bot pushed a commit that referenced this pull request May 9, 2024
This is the first step to experimenting with a new type of stack traces
behind the `enableOwnerStacks` flag - in DEV only.

The idea is to generate stacks that are more like if the JSX was a
direct call even though it's actually a lazy call. Not only can you see
which exact JSX call line number generated the erroring component but if
that's inside an abstraction function, which function called that
function and if it's a component, which component generated that
component. For this to make sense it really need to be the "owner" stack
rather than the parent stack like we do for other component stacks. On
one hand it has more precise information but on the other hand it also
loses context. For most types of problems the owner stack is the most
useful though since it tells you which component rendered this
component.

The problem with the platform in its current state is that there's two
ways to deal with stacks:

1) `new Error().stack`
2) `console.createTask()`

The nice thing about `new Error().stack` is that we can extract the
frames and piece them together in whatever way we want. That is great
for constructing custom UIs like error dialogs. Unfortunately, we can't
take custom stacks and set them in the native UIs like Chrome DevTools.

The nice thing about `console.createTask()` is that the resulting stacks
are natively integrated into the Chrome DevTools in the console and the
breakpoint debugger. They also automatically follow source mapping and
ignoreLists. The downside is that there's no way to extract the async
stack outside the native UI itself so this information cannot be used
for custom UIs like errors dialogs. It also means we can't collect this
on the server and then pass it to the client for server components.

The solution here is that we use both techniques and collect both an
`Error` object and a `Task` object for every JSX call.

The main concern about this approach is the performance so that's the
main thing to test. It's certainly too slow for production but it might
also be too slow even for DEV.

This first PR doesn't actually use the stacks yet. It just collects them
as the first step. The next step is to start utilizing this information
in error printing etc.

For RSC we pass the stack along across over the wire. This can be
concatenated on the client following the owner path to create an owner
stack leading back into the server. We'll later use this information to
restore fake frames on the client for native integration. Since this
information quickly gets pretty heavy if we include all frames, we strip
out the top frame. We also strip out everything below the functions that
call into user space in the Flight runtime. To do this we need to figure
out the frames that represents calling out into user space. The
resulting stack is typically just the one frame inside the owner
component's JSX callsite. I also eagerly strip out things we expect to
be ignoreList:ed anyway - such as `node_modules` and Node.js internals.

DiffTrain build for commit 151cce3.
github-actions bot pushed a commit that referenced this pull request May 9, 2024
This is the first step to experimenting with a new type of stack traces
behind the `enableOwnerStacks` flag - in DEV only.

The idea is to generate stacks that are more like if the JSX was a
direct call even though it's actually a lazy call. Not only can you see
which exact JSX call line number generated the erroring component but if
that's inside an abstraction function, which function called that
function and if it's a component, which component generated that
component. For this to make sense it really need to be the "owner" stack
rather than the parent stack like we do for other component stacks. On
one hand it has more precise information but on the other hand it also
loses context. For most types of problems the owner stack is the most
useful though since it tells you which component rendered this
component.

The problem with the platform in its current state is that there's two
ways to deal with stacks:

1) `new Error().stack`
2) `console.createTask()`

The nice thing about `new Error().stack` is that we can extract the
frames and piece them together in whatever way we want. That is great
for constructing custom UIs like error dialogs. Unfortunately, we can't
take custom stacks and set them in the native UIs like Chrome DevTools.

The nice thing about `console.createTask()` is that the resulting stacks
are natively integrated into the Chrome DevTools in the console and the
breakpoint debugger. They also automatically follow source mapping and
ignoreLists. The downside is that there's no way to extract the async
stack outside the native UI itself so this information cannot be used
for custom UIs like errors dialogs. It also means we can't collect this
on the server and then pass it to the client for server components.

The solution here is that we use both techniques and collect both an
`Error` object and a `Task` object for every JSX call.

The main concern about this approach is the performance so that's the
main thing to test. It's certainly too slow for production but it might
also be too slow even for DEV.

This first PR doesn't actually use the stacks yet. It just collects them
as the first step. The next step is to start utilizing this information
in error printing etc.

For RSC we pass the stack along across over the wire. This can be
concatenated on the client following the owner path to create an owner
stack leading back into the server. We'll later use this information to
restore fake frames on the client for native integration. Since this
information quickly gets pretty heavy if we include all frames, we strip
out the top frame. We also strip out everything below the functions that
call into user space in the Flight runtime. To do this we need to figure
out the frames that represents calling out into user space. The
resulting stack is typically just the one frame inside the owner
component's JSX callsite. I also eagerly strip out things we expect to
be ignoreList:ed anyway - such as `node_modules` and Node.js internals.

DiffTrain build for [151cce3](151cce3)
}
// TODO: We should be freezing the element but currently, we might write into
// _debugInfo later. We could move it into _store which remains mutable.
Object.freeze(element.props);
Copy link
Collaborator

Choose a reason for hiding this comment

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

On a Next.js client side navigations, we get the following RSC payload:

3:I["(app-pages-browser)/../../../../packages/next/dist/client/components/client-page.js",["app-pages-internals","static/chunks/app-pages-internals.js"],"ClientPageRoot"]
4:I["(app-pages-browser)/./app/[id]/page.tsx",["app/[id]/page","static/chunks/app/%5Bid%5D/page.js"],"default",1]
5:I["(app-pages-browser)/../../../../packages/next/dist/client/components/layout-router.js",["app-pages-internals","static/chunks/app-pages-internals.js"],""]
6:I["(app-pages-browser)/../../../../packages/next/dist/client/components/render-from-template-context.js",["app-pages-internals","static/chunks/app-pages-internals.js"],""]
2:{"name":"","env":"Server","owner":null}
1:D"$2"
8:{"name":"","env":"Server","owner":null}
7:D"$8"
0:["development",[["children",["id","a","d"],[["id","a","d"],{"children":["__PAGE__",{}]}],[["id","a","d"],{"children":["__PAGE__",{},[["$L1",["$","$L3",null,{"props":{"params":{"id":"a"},"searchParams":{}},"Component":"$4"},null]],null],null]},["$","$L5",null,{"parallelRouterKey":"children","segmentPath":["children","$0:1:0:3:0","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{},null],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null},null],null],[null,"$L7"]]]]
9:"$Sreact.fragment"
7:["$","$9","19VxPb-E-LbHeAfz9ZCJ2",{"children":[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"},null],["$","meta","1",{"charSet":"utf-8"},null]]}]
1:null

Which then results in ""Cannot assign to read only property 'Component' of object '#'" thrown from

Object.freeze(element.props);

I don't have a smaller repro yet. Is this a bug with the Flight Server, our usage or should we just not freeze the props on the Flight Client?

Copy link

@hi-ogawa hi-ogawa May 17, 2024

Choose a reason for hiding this comment

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

I think I'm seeing a similar issue here hi-ogawa/vite-plugins#318 I don't have a repro outside of my framework yet, but the error seems related to passing client reference as client component props like below:

  • page.tsx
import { Client1, Client2 } from "./_client";

export default function Page() {
  return (
    <div>
      <h4 className="font-bold">Repro</h4>
      {/* TypeError: Cannot assign to read only property 'SomeComp' of object '#<Object>' */}
      <Client1 SomeComp={Client2} />
    </div>
  );
}
  • _client.tsx
"use client";

import React from "react";

export function Client1(props: { SomeComp: React.ComponentType }) {
  return (
    <div>
      [Client1] props.SomeComp = <props.SomeComp />
    </div>
  );
}

export function Client2() {
  return <span>[Client2]</span>;
}

I'm using a similar pattern for Error boundary and flight client seems to be throwing for this.

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.

6 participants