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

Add renderToMarkup for Client Components #30121

Merged
merged 6 commits into from
Jun 28, 2024
Merged

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Jun 28, 2024

Follow up to #30105.

This supports renderToMarkup in a non-RSC environment (not the react-server condition).

This is just a Fizz renderer but it errors at runtime when you use state, effects or event handlers that would require hydration - like the RSC version would. (Except RSC can give early errors too.)

To do this I have to move the react-html builds to a new markup dimension out of the dom-legacy dimension so that we can configure this differently from renderToString/renderToStaticMarkup. Eventually that dimension can go away though if deprecated. That also helps us avoid dynamic configuration and we can just compile in the right configuration so the split helps anyway.

One consideration is that if a compiler strips out useEffects or inlines initial state from useState, then it would not get called an the error wouldn't happen. Therefore to preserve semantics, a compiler would need to inject some call that can check the current renderer and whether it should throw.

There is an argument that it could be useful to not error for these because it's possible to write components that works with SSR but are just optionally hydrated. However, there's also an argument that doing that silently is too easy to lead to mistakes and it's better to error - especially for the e-mail use case where you can't take it back but you can replay a queue that had failures. There are other ways to conditionally branch components intentionally. Besides if you want it to be silent you can still use renderToString (or better yet renderToReadableStream).

The primary mechanism is the RSC environment and the client-environment is really the secondary one that's only there to support legacy environments. So this also ensures parity with the primary environment.

This lets you use renderToMarkup in a Client Component environment as a
replacement to renderToStaticMarkup.
That way we can have separate configurations for legacy renderers
(renderToString/renderToStaticMarkup) and the modern renderToMarkup.
Unlike the dom-legacy config this isn't using a dynamic flag for determining
whether it is unhydrated markup.
These prevent mistakes when a component is expected to be hydrated.
Copy link

vercel bot commented Jun 28, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
react-compiler-playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 28, 2024 1:29am
@react-sizebot
Copy link

react-sizebot commented Jun 28, 2024

Comparing: e02baf6...8b8c255

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.68 kB 6.68 kB +0.11% 1.83 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 497.99 kB 497.99 kB = 89.27 kB 89.27 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB +0.05% 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 502.81 kB 502.81 kB = 89.97 kB 89.97 kB
facebook-www/ReactDOM-prod.classic.js = 597.08 kB 597.08 kB = 105.33 kB 105.33 kB
facebook-www/ReactDOM-prod.modern.js = 571.42 kB 571.42 kB = 101.27 kB 101.27 kB
oss-experimental/react-html/cjs/react-html.development.js +∞% 0.00 kB 334.45 kB +∞% 0.00 kB 61.10 kB
oss-experimental/react-html/cjs/react-html.production.js +∞% 0.00 kB 202.60 kB +∞% 0.00 kB 38.01 kB
oss-stable-rc/react-html/cjs/react-html.development.js +∞% 0.00 kB 313.75 kB +∞% 0.00 kB 58.50 kB
oss-stable-rc/react-html/cjs/react-html.production.js +∞% 0.00 kB 186.48 kB +∞% 0.00 kB 35.80 kB
oss-stable-semver/react-html/cjs/react-html.development.js +∞% 0.00 kB 313.75 kB +∞% 0.00 kB 58.50 kB
oss-stable-semver/react-html/cjs/react-html.production.js +∞% 0.00 kB 186.48 kB +∞% 0.00 kB 35.80 kB
oss-stable/react-html/cjs/react-html.development.js +∞% 0.00 kB 313.78 kB +∞% 0.00 kB 58.52 kB
oss-stable/react-html/cjs/react-html.production.js +∞% 0.00 kB 186.50 kB +∞% 0.00 kB 35.82 kB
oss-experimental/react-html/index.js +71.93% 0.11 kB 0.20 kB +24.14% 0.12 kB 0.14 kB
oss-stable-rc/react-html/index.js +71.93% 0.11 kB 0.20 kB +24.14% 0.12 kB 0.14 kB
oss-stable-semver/react-html/index.js +71.93% 0.11 kB 0.20 kB +24.14% 0.12 kB 0.14 kB
oss-stable/react-html/index.js +71.93% 0.11 kB 0.20 kB +24.14% 0.12 kB 0.14 kB
test_utils/ReactAllWarnings.js Deleted 62.88 kB 0.00 kB Deleted 15.69 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-html/cjs/react-html.development.js +∞% 0.00 kB 334.45 kB +∞% 0.00 kB 61.10 kB
oss-experimental/react-html/cjs/react-html.production.js +∞% 0.00 kB 202.60 kB +∞% 0.00 kB 38.01 kB
oss-stable-rc/react-html/cjs/react-html.development.js +∞% 0.00 kB 313.75 kB +∞% 0.00 kB 58.50 kB
oss-stable-rc/react-html/cjs/react-html.production.js +∞% 0.00 kB 186.48 kB +∞% 0.00 kB 35.80 kB
oss-stable-semver/react-html/cjs/react-html.development.js +∞% 0.00 kB 313.75 kB +∞% 0.00 kB 58.50 kB
oss-stable-semver/react-html/cjs/react-html.production.js +∞% 0.00 kB 186.48 kB +∞% 0.00 kB 35.80 kB
oss-stable/react-html/cjs/react-html.development.js +∞% 0.00 kB 313.78 kB +∞% 0.00 kB 58.52 kB
oss-stable/react-html/cjs/react-html.production.js +∞% 0.00 kB 186.50 kB +∞% 0.00 kB 35.82 kB
oss-experimental/react-html/index.js +71.93% 0.11 kB 0.20 kB +24.14% 0.12 kB 0.14 kB
oss-stable-rc/react-html/index.js +71.93% 0.11 kB 0.20 kB +24.14% 0.12 kB 0.14 kB
oss-stable-semver/react-html/index.js +71.93% 0.11 kB 0.20 kB +24.14% 0.12 kB 0.14 kB
oss-stable/react-html/index.js +71.93% 0.11 kB 0.20 kB +24.14% 0.12 kB 0.14 kB
oss-experimental/react-html/cjs/react-html.react-server.production.js = 301.59 kB 299.27 kB = 57.10 kB 56.57 kB
oss-experimental/react-html/cjs/react-html.react-server.development.js = 481.13 kB 477.05 kB = 86.93 kB 86.28 kB
oss-stable/react-html/cjs/react-html.react-server.development.js = 443.42 kB 438.47 kB = 80.76 kB 80.03 kB
oss-stable-rc/react-html/cjs/react-html.react-server.development.js = 443.39 kB 438.45 kB = 80.74 kB 80.00 kB
oss-stable-semver/react-html/cjs/react-html.react-server.development.js = 443.39 kB 438.45 kB = 80.74 kB 80.00 kB
oss-stable/react-html/cjs/react-html.react-server.production.js = 282.13 kB 278.90 kB = 54.28 kB 53.66 kB
oss-stable-rc/react-html/cjs/react-html.react-server.production.js = 282.10 kB 278.88 kB = 54.25 kB 53.64 kB
oss-stable-semver/react-html/cjs/react-html.react-server.production.js = 282.10 kB 278.88 kB = 54.25 kB 53.64 kB
test_utils/ReactAllWarnings.js Deleted 62.88 kB 0.00 kB Deleted 15.69 kB 0.00 kB

Generated by 🚫 dangerJS against 8b8c255

Otherwise we end up throwing since the Fizz implementation doesn't have one,
or if it did, we'd fail the test since it was enabled.
@sebmarkbage sebmarkbage merged commit 1e241f9 into facebook:main Jun 28, 2024
139 checks passed
github-actions bot pushed a commit that referenced this pull request Jun 28, 2024
Follow up to #30105.

This supports `renderToMarkup` in a non-RSC environment (not the
`react-server` condition).

This is just a Fizz renderer but it errors at runtime when you use
state, effects or event handlers that would require hydration - like the
RSC version would. (Except RSC can give early errors too.)

To do this I have to move the `react-html` builds to a new `markup`
dimension out of the `dom-legacy` dimension so that we can configure
this differently from `renderToString`/`renderToStaticMarkup`.
Eventually that dimension can go away though if deprecated. That also
helps us avoid dynamic configuration and we can just compile in the
right configuration so the split helps anyway.

One consideration is that if a compiler strips out useEffects or inlines
initial state from useState, then it would not get called an the error
wouldn't happen. Therefore to preserve semantics, a compiler would need
to inject some call that can check the current renderer and whether it
should throw.

There is an argument that it could be useful to not error for these
because it's possible to write components that works with SSR but are
just optionally hydrated. However, there's also an argument that doing
that silently is too easy to lead to mistakes and it's better to error -
especially for the e-mail use case where you can't take it back but you
can replay a queue that had failures. There are other ways to
conditionally branch components intentionally. Besides if you want it to
be silent you can still use renderToString (or better yet
renderToReadableStream).

The primary mechanism is the RSC environment and the client-environment
is really the secondary one that's only there to support legacy
environments. So this also ensures parity with the primary environment.

DiffTrain build for commit 1e241f9.
sebmarkbage added a commit that referenced this pull request Jun 28, 2024
Stacked on top of #30121.

This is the same thing we do for `renderToReadableStream` so that you
don't have to manually inject it into the stream.

The only reason we didn't for `renderToString` / `renderToStaticMarkup`
was to preserve legacy behavior but since this is a new API we can
change that.

If you're rendering a partial it doesn't matter. This is likely what
you'd do for RSS feeds. The question is if you can reliably rely on the
doctype being used while rendering e-mails since many clients are so
quirky. However, if you're careful it also doesn't hurt so it seems best
to include it.
);
});

// @gate disableClientCache
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 took me a while to figure out that disableClientCache is hard coded in the test flags and not derived from other flags. Couldn't figure out why it was only failing one config.

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
4 participants