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

Scaffolding for <Catch> #26854

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Conversation

acdlite
Copy link
Collaborator

@acdlite acdlite commented May 25, 2023

Exposes the component (behind a flag) and teaches Fiber/Fizz to recognize it. Basically everything except the actual error handling behavior; it gets rendered as a fragment for now.

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels May 25, 2023
@acdlite acdlite force-pushed the scaffolding-for-catch branch 3 times, most recently from beaa84e to ccc3a53 Compare May 25, 2023 17:39
@react-sizebot
Copy link

react-sizebot commented May 25, 2023

Comparing: a1f9758...cb060e8

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 +0.04% 164.23 kB 164.30 kB +0.02% 51.77 kB 51.78 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js +0.15% 171.67 kB 171.92 kB +0.14% 54.01 kB 54.08 kB
facebook-www/ReactDOM-prod.classic.js +0.02% 570.42 kB 570.55 kB +0.04% 100.64 kB 100.68 kB
facebook-www/ReactDOM-prod.modern.js +0.02% 554.16 kB 554.29 kB +0.04% 97.82 kB 97.86 kB
oss-experimental/react-is/cjs/react-is.production.min.js +3.70% 2.30 kB 2.39 kB +2.40% 0.79 kB 0.81 kB
oss-experimental/react-is/umd/react-is.production.min.js +3.56% 2.39 kB 2.47 kB +2.59% 0.89 kB 0.91 kB
oss-experimental/react/cjs/react.production.min.js +2.52% 9.16 kB 9.39 kB +1.38% 3.42 kB 3.46 kB
oss-experimental/react-is/cjs/react-is.development.js +2.52% 7.35 kB 7.54 kB +1.64% 2.01 kB 2.04 kB
oss-experimental/react-is/umd/react-is.development.js +2.38% 7.95 kB 8.14 kB +1.42% 2.11 kB 2.14 kB
oss-stable-semver/react-is/cjs/react-is.development.js +2.06% 7.33 kB 7.48 kB +2.64% 2.01 kB 2.06 kB
oss-stable/react-is/cjs/react-is.development.js +2.06% 7.33 kB 7.48 kB +2.64% 2.01 kB 2.06 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-is/cjs/react-is.production.min.js +3.70% 2.30 kB 2.39 kB +2.40% 0.79 kB 0.81 kB
oss-experimental/react-is/umd/react-is.production.min.js +3.56% 2.39 kB 2.47 kB +2.59% 0.89 kB 0.91 kB
oss-experimental/react/cjs/react.production.min.js +2.52% 9.16 kB 9.39 kB +1.38% 3.42 kB 3.46 kB
oss-experimental/react-is/cjs/react-is.development.js +2.52% 7.35 kB 7.54 kB +1.64% 2.01 kB 2.04 kB
oss-experimental/react-is/umd/react-is.development.js +2.38% 7.95 kB 8.14 kB +1.42% 2.11 kB 2.14 kB
oss-stable-semver/react-is/cjs/react-is.development.js +2.06% 7.33 kB 7.48 kB +2.64% 2.01 kB 2.06 kB
oss-stable/react-is/cjs/react-is.development.js +2.06% 7.33 kB 7.48 kB +2.64% 2.01 kB 2.06 kB
oss-stable-semver/react-is/umd/react-is.development.js +1.93% 7.93 kB 8.08 kB +2.47% 2.10 kB 2.15 kB
oss-stable/react-is/umd/react-is.development.js +1.93% 7.93 kB 8.08 kB +2.47% 2.10 kB 2.15 kB
oss-experimental/react/umd/react.profiling.min.js +1.67% 12.98 kB 13.20 kB +1.12% 4.99 kB 5.04 kB
oss-experimental/react/umd/react.production.min.js +1.67% 12.98 kB 13.20 kB +1.14% 4.98 kB 5.04 kB
facebook-www/React-prod.modern.js +1.23% 21.30 kB 21.56 kB +0.83% 5.31 kB 5.35 kB
facebook-react-native/react/cjs/React-prod.js +1.23% 21.33 kB 21.59 kB +0.79% 5.35 kB 5.39 kB
facebook-www/React-prod.classic.js +1.22% 21.60 kB 21.86 kB +0.80% 5.38 kB 5.42 kB
facebook-react-native/react/cjs/React-profiling.js +1.22% 21.64 kB 21.91 kB +0.81% 5.40 kB 5.44 kB
facebook-www/React-profiling.modern.js +1.20% 21.91 kB 22.17 kB +0.85% 5.43 kB 5.48 kB
facebook-www/React-profiling.classic.js +1.18% 22.20 kB 22.46 kB +0.82% 5.51 kB 5.55 kB
facebook-react-native/react-is/cjs/ReactIs-dev.js +1.14% 7.29 kB 7.38 kB +1.12% 1.97 kB 1.99 kB
facebook-www/ReactIs-dev.modern.js +1.08% 7.66 kB 7.75 kB +1.08% 2.03 kB 2.06 kB
facebook-www/ReactIs-dev.classic.js +1.08% 7.67 kB 7.75 kB +1.03% 2.03 kB 2.06 kB
oss-experimental/react/cjs/react.shared-subset.production.min.js +0.72% 8.35 kB 8.41 kB +0.52% 3.44 kB 3.46 kB
oss-experimental/react-server/cjs/react-server.production.min.js +0.55% 25.39 kB 25.53 kB +0.32% 8.65 kB 8.68 kB
oss-experimental/react/cjs/react.development.js +0.53% 101.58 kB 102.12 kB +0.47% 27.41 kB 27.54 kB
oss-experimental/react/umd/react.development.js +0.45% 125.22 kB 125.78 kB +0.41% 32.29 kB 32.42 kB
oss-experimental/react/cjs/react-jsx-dev-runtime.development.js +0.43% 42.93 kB 43.12 kB +0.27% 12.52 kB 12.56 kB
oss-experimental/react/cjs/react-jsx-runtime.development.js +0.43% 43.53 kB 43.71 kB +0.26% 12.70 kB 12.73 kB
facebook-react-native/react/cjs/React-dev.js +0.36% 120.04 kB 120.48 kB +0.36% 31.60 kB 31.72 kB
oss-stable-semver/react/cjs/react-jsx-dev-runtime.development.js +0.35% 42.96 kB 43.11 kB +0.37% 12.53 kB 12.58 kB
oss-stable/react/cjs/react-jsx-dev-runtime.development.js +0.35% 42.96 kB 43.11 kB +0.37% 12.53 kB 12.58 kB
oss-experimental/react/cjs/react.shared-subset.development.js +0.35% 85.70 kB 85.99 kB +0.35% 23.79 kB 23.87 kB
oss-stable-semver/react/cjs/react-jsx-runtime.development.js +0.35% 43.56 kB 43.71 kB +0.36% 12.70 kB 12.75 kB
oss-stable/react/cjs/react-jsx-runtime.development.js +0.35% 43.56 kB 43.71 kB +0.36% 12.70 kB 12.75 kB
facebook-www/React-dev.modern.js +0.34% 126.66 kB 127.10 kB +0.40% 33.45 kB 33.59 kB
facebook-www/React-dev.classic.js +0.34% 127.76 kB 128.19 kB +0.40% 33.70 kB 33.83 kB
oss-experimental/react-art/cjs/react-art.production.min.js +0.25% 98.26 kB 98.51 kB +0.15% 30.15 kB 30.20 kB
oss-experimental/react-server/cjs/react-server.development.js +0.24% 147.64 kB 148.00 kB +0.05% 36.78 kB 36.80 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +0.23% 61.54 kB 61.68 kB +0.16% 18.51 kB 18.54 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.production.min.js +0.23% 61.71 kB 61.85 kB +0.20% 18.78 kB 18.82 kB
oss-experimental/react-dom/cjs/react-dom-static.browser.production.min.js +0.23% 62.22 kB 62.36 kB +0.18% 19.26 kB 19.30 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.min.js +0.23% 62.34 kB 62.48 kB +0.17% 19.32 kB 19.35 kB
oss-experimental/react-dom/cjs/react-dom-static.edge.production.min.js +0.23% 62.56 kB 62.70 kB +0.15% 19.39 kB 19.42 kB
facebook-react-native/react/cjs/JSXDEVRuntime-dev.js +0.22% 37.02 kB 37.10 kB +0.17% 10.66 kB 10.68 kB
oss-experimental/react-dom/umd/react-dom-server.browser.production.min.js +0.22% 62.50 kB 62.64 kB +0.12% 19.57 kB 19.60 kB
facebook-react-native/react/cjs/JSXRuntime-dev.js +0.22% 37.62 kB 37.70 kB +0.23% 10.83 kB 10.85 kB
oss-experimental/react-reconciler/cjs/react-reconciler.production.min.js +0.22% 115.19 kB 115.44 kB +0.12% 35.18 kB 35.22 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.min.js +0.21% 65.41 kB 65.55 kB +0.13% 20.03 kB 20.06 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.min.js +0.21% 66.40 kB 66.54 kB +0.10% 20.07 kB 20.10 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.production.min.js +0.21% 66.69 kB 66.83 kB +0.12% 20.81 kB 20.83 kB
oss-experimental/react-dom/cjs/react-dom-static.node.production.min.js +0.21% 66.79 kB 66.93 kB +0.12% 20.86 kB 20.89 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.min.js +0.21% 66.83 kB 66.97 kB +0.15% 20.88 kB 20.91 kB
oss-experimental/react-reconciler/cjs/react-reconciler.profiling.min.js +0.20% 124.20 kB 124.45 kB +0.17% 37.36 kB 37.42 kB

Generated by 🚫 dangerJS against cb060e8

acdlite added 2 commits May 27, 2023 14:47
Adds an experimental feature flag that will be used to gate the upcoming
<Catch> API, as well as `createCatch` and `raise`.
Exposes the <Catch> component (behind a flag) and teachs Fiber/Fizz to
recognize it. Basically everything except the actual error handling
behavior; it gets rendered as a fragment for now.
@baked-dev
Copy link

Is this stale or what is the plan with this? Looking to get that next issue referenced above unblocked @acdlite

@danieljpgo
Copy link

image

@sebmarkbage sebmarkbage mentioned this pull request Aug 17, 2023
sebmarkbage added a commit that referenced this pull request Aug 17, 2023
This adds an experimental `unstable_postpone(reason)` API.

Currently we don't have a way to model effectively an Infinite Promise.
I.e. something that suspends but never resolves. The reason this is
useful is because you might have something else that unblocks it later.
E.g. by updating in place later, or by client rendering.

On the client this works to model as an Infinite Promise (in fact,
that's what this implementation does). However, in Fizz and Flight that
doesn't work because the stream needs to end at some point. We don't
have any way of knowing that we're suspended on infinite promises. It's
not enough to tag the promises because you could await those and thus
creating new promises. The only way we really have to signal this
through a series of indirections like async functions, is by throwing.
It's not 100% safe because these values can be caught but it's the best
we can do.

Effectively `postpone(reason)` behaves like a built-in [Catch
Boundary](#26854). It's like
`raise(Postpone, reason)` except it's built-in so it needs to be able to
be encoded and caught by Suspense boundaries.

In Flight and Fizz these behave pretty much the same as errors. Flight
just forwards it to retrigger on the client. In Fizz they just trigger
client rendering which itself might just postpone again or fill in the
value. The difference is how they get logged.

In Flight and Fizz they log to `onPostpone(reason)` instead of
`onError(error)`. This log is meant to help find deopts on the server
like finding places where you fall back to client rendering. The reason
that you pass in is for that purpose to help the reason for any deopts.

I do track the stack trace in DEV but I don't currently expose it to
`onPostpone`. This seems like a limitation. It might be better to expose
the Postpone object which is an Error object but that's more of an
implementation detail. I could also pass it as a second argument.

On the client after hydration they don't get passed to
`onRecoverableError`. There's no global `onPostpone` API to capture
postponed things on the client just like there's no `onError`. At that
point it's just assumed to be intentional. It doesn't have any `digest`
or reason passed to the client since it's not logged.

There are some hacky solutions that currently just tries to reuse as
much of the existing code as possible but should be more properly
implemented.
- Fiber is currently just converting it to a fake Promise object so that
it behaves like an infinite Promise.
- Fizz is encoding the magic digest string `"POSTPONE"` in the HTML so
we know to ignore it but it should probably just be something neater
that doesn't share namespace with digests.

Next I plan on using this in the `/static` entry points for additional
features.

Why "postpone"? It's basically a synonym to "defer" but we plan on using
"defer" for other purposes and it's overloaded anyway.
github-actions bot pushed a commit that referenced this pull request Aug 17, 2023
This adds an experimental `unstable_postpone(reason)` API.

Currently we don't have a way to model effectively an Infinite Promise.
I.e. something that suspends but never resolves. The reason this is
useful is because you might have something else that unblocks it later.
E.g. by updating in place later, or by client rendering.

On the client this works to model as an Infinite Promise (in fact,
that's what this implementation does). However, in Fizz and Flight that
doesn't work because the stream needs to end at some point. We don't
have any way of knowing that we're suspended on infinite promises. It's
not enough to tag the promises because you could await those and thus
creating new promises. The only way we really have to signal this
through a series of indirections like async functions, is by throwing.
It's not 100% safe because these values can be caught but it's the best
we can do.

Effectively `postpone(reason)` behaves like a built-in [Catch
Boundary](#26854). It's like
`raise(Postpone, reason)` except it's built-in so it needs to be able to
be encoded and caught by Suspense boundaries.

In Flight and Fizz these behave pretty much the same as errors. Flight
just forwards it to retrigger on the client. In Fizz they just trigger
client rendering which itself might just postpone again or fill in the
value. The difference is how they get logged.

In Flight and Fizz they log to `onPostpone(reason)` instead of
`onError(error)`. This log is meant to help find deopts on the server
like finding places where you fall back to client rendering. The reason
that you pass in is for that purpose to help the reason for any deopts.

I do track the stack trace in DEV but I don't currently expose it to
`onPostpone`. This seems like a limitation. It might be better to expose
the Postpone object which is an Error object but that's more of an
implementation detail. I could also pass it as a second argument.

On the client after hydration they don't get passed to
`onRecoverableError`. There's no global `onPostpone` API to capture
postponed things on the client just like there's no `onError`. At that
point it's just assumed to be intentional. It doesn't have any `digest`
or reason passed to the client since it's not logged.

There are some hacky solutions that currently just tries to reuse as
much of the existing code as possible but should be more properly
implemented.
- Fiber is currently just converting it to a fake Promise object so that
it behaves like an infinite Promise.
- Fizz is encoding the magic digest string `"POSTPONE"` in the HTML so
we know to ignore it but it should probably just be something neater
that doesn't share namespace with digests.

Next I plan on using this in the `/static` entry points for additional
features.

Why "postpone"? It's basically a synonym to "defer" but we plan on using
"defer" for other purposes and it's overloaded anyway.

DiffTrain build for [ac1a16c](ac1a16c)
jerrydev0927 added a commit to jerrydev0927/react that referenced this pull request Jan 5, 2024
This adds an experimental `unstable_postpone(reason)` API.

Currently we don't have a way to model effectively an Infinite Promise.
I.e. something that suspends but never resolves. The reason this is
useful is because you might have something else that unblocks it later.
E.g. by updating in place later, or by client rendering.

On the client this works to model as an Infinite Promise (in fact,
that's what this implementation does). However, in Fizz and Flight that
doesn't work because the stream needs to end at some point. We don't
have any way of knowing that we're suspended on infinite promises. It's
not enough to tag the promises because you could await those and thus
creating new promises. The only way we really have to signal this
through a series of indirections like async functions, is by throwing.
It's not 100% safe because these values can be caught but it's the best
we can do.

Effectively `postpone(reason)` behaves like a built-in [Catch
Boundary](facebook/react#26854). It's like
`raise(Postpone, reason)` except it's built-in so it needs to be able to
be encoded and caught by Suspense boundaries.

In Flight and Fizz these behave pretty much the same as errors. Flight
just forwards it to retrigger on the client. In Fizz they just trigger
client rendering which itself might just postpone again or fill in the
value. The difference is how they get logged.

In Flight and Fizz they log to `onPostpone(reason)` instead of
`onError(error)`. This log is meant to help find deopts on the server
like finding places where you fall back to client rendering. The reason
that you pass in is for that purpose to help the reason for any deopts.

I do track the stack trace in DEV but I don't currently expose it to
`onPostpone`. This seems like a limitation. It might be better to expose
the Postpone object which is an Error object but that's more of an
implementation detail. I could also pass it as a second argument.

On the client after hydration they don't get passed to
`onRecoverableError`. There's no global `onPostpone` API to capture
postponed things on the client just like there's no `onError`. At that
point it's just assumed to be intentional. It doesn't have any `digest`
or reason passed to the client since it's not logged.

There are some hacky solutions that currently just tries to reuse as
much of the existing code as possible but should be more properly
implemented.
- Fiber is currently just converting it to a fake Promise object so that
it behaves like an infinite Promise.
- Fizz is encoding the magic digest string `"POSTPONE"` in the HTML so
we know to ignore it but it should probably just be something neater
that doesn't share namespace with digests.

Next I plan on using this in the `/static` entry points for additional
features.

Why "postpone"? It's basically a synonym to "defer" but we plan on using
"defer" for other purposes and it's overloaded anyway.

DiffTrain build for [ac1a16c67e268fcb2c52e91717cbc918c7c24446](facebook/react@ac1a16c)
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
This adds an experimental `unstable_postpone(reason)` API.

Currently we don't have a way to model effectively an Infinite Promise.
I.e. something that suspends but never resolves. The reason this is
useful is because you might have something else that unblocks it later.
E.g. by updating in place later, or by client rendering.

On the client this works to model as an Infinite Promise (in fact,
that's what this implementation does). However, in Fizz and Flight that
doesn't work because the stream needs to end at some point. We don't
have any way of knowing that we're suspended on infinite promises. It's
not enough to tag the promises because you could await those and thus
creating new promises. The only way we really have to signal this
through a series of indirections like async functions, is by throwing.
It's not 100% safe because these values can be caught but it's the best
we can do.

Effectively `postpone(reason)` behaves like a built-in [Catch
Boundary](facebook#26854). It's like
`raise(Postpone, reason)` except it's built-in so it needs to be able to
be encoded and caught by Suspense boundaries.

In Flight and Fizz these behave pretty much the same as errors. Flight
just forwards it to retrigger on the client. In Fizz they just trigger
client rendering which itself might just postpone again or fill in the
value. The difference is how they get logged.

In Flight and Fizz they log to `onPostpone(reason)` instead of
`onError(error)`. This log is meant to help find deopts on the server
like finding places where you fall back to client rendering. The reason
that you pass in is for that purpose to help the reason for any deopts.

I do track the stack trace in DEV but I don't currently expose it to
`onPostpone`. This seems like a limitation. It might be better to expose
the Postpone object which is an Error object but that's more of an
implementation detail. I could also pass it as a second argument.

On the client after hydration they don't get passed to
`onRecoverableError`. There's no global `onPostpone` API to capture
postponed things on the client just like there's no `onError`. At that
point it's just assumed to be intentional. It doesn't have any `digest`
or reason passed to the client since it's not logged.

There are some hacky solutions that currently just tries to reuse as
much of the existing code as possible but should be more properly
implemented.
- Fiber is currently just converting it to a fake Promise object so that
it behaves like an infinite Promise.
- Fizz is encoding the magic digest string `"POSTPONE"` in the HTML so
we know to ignore it but it should probably just be something neater
that doesn't share namespace with digests.

Next I plan on using this in the `/static` entry points for additional
features.

Why "postpone"? It's basically a synonym to "defer" but we plan on using
"defer" for other purposes and it's overloaded anyway.
Akshato07 pushed a commit to Akshato07/-Luffy that referenced this pull request Feb 20, 2025
This adds an experimental `unstable_postpone(reason)` API.

Currently we don't have a way to model effectively an Infinite Promise.
I.e. something that suspends but never resolves. The reason this is
useful is because you might have something else that unblocks it later.
E.g. by updating in place later, or by client rendering.

On the client this works to model as an Infinite Promise (in fact,
that's what this implementation does). However, in Fizz and Flight that
doesn't work because the stream needs to end at some point. We don't
have any way of knowing that we're suspended on infinite promises. It's
not enough to tag the promises because you could await those and thus
creating new promises. The only way we really have to signal this
through a series of indirections like async functions, is by throwing.
It's not 100% safe because these values can be caught but it's the best
we can do.

Effectively `postpone(reason)` behaves like a built-in [Catch
Boundary](facebook/react#26854). It's like
`raise(Postpone, reason)` except it's built-in so it needs to be able to
be encoded and caught by Suspense boundaries.

In Flight and Fizz these behave pretty much the same as errors. Flight
just forwards it to retrigger on the client. In Fizz they just trigger
client rendering which itself might just postpone again or fill in the
value. The difference is how they get logged.

In Flight and Fizz they log to `onPostpone(reason)` instead of
`onError(error)`. This log is meant to help find deopts on the server
like finding places where you fall back to client rendering. The reason
that you pass in is for that purpose to help the reason for any deopts.

I do track the stack trace in DEV but I don't currently expose it to
`onPostpone`. This seems like a limitation. It might be better to expose
the Postpone object which is an Error object but that's more of an
implementation detail. I could also pass it as a second argument.

On the client after hydration they don't get passed to
`onRecoverableError`. There's no global `onPostpone` API to capture
postponed things on the client just like there's no `onError`. At that
point it's just assumed to be intentional. It doesn't have any `digest`
or reason passed to the client since it's not logged.

There are some hacky solutions that currently just tries to reuse as
much of the existing code as possible but should be more properly
implemented.
- Fiber is currently just converting it to a fake Promise object so that
it behaves like an infinite Promise.
- Fizz is encoding the magic digest string `"POSTPONE"` in the HTML so
we know to ignore it but it should probably just be something neater
that doesn't share namespace with digests.

Next I plan on using this in the `/static` entry points for additional
features.

Why "postpone"? It's basically a synonym to "defer" but we plan on using
"defer" for other purposes and it's overloaded anyway.

DiffTrain build for commit facebook/react@ac1a16c.
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.

5 participants