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 a way to create Server Reference Proxies on the client #26632

Merged
merged 1 commit into from
Apr 15, 2023

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Apr 15, 2023

This lets the client bundle encode Server References without them first being passed from an RSC payload. Like if you just import "use server" from the client. A bundler could already emit these proxies to be called on the client but the subtle difference is that those proxies couldn't be passed back into the server by reference. They have to be registered with React.

We don't currently implement importing "use server" from client components in the reference implementation. It'd need to expand the Webpack plugin with a loader that rewrites files with the "use server" in the client bundle.

"use server";

export async function action() {
   ...
}

->

import {createServerReference} from "react-server-dom-webpack/client";
import {callServer} from "some-router/call-server";

export const action = createServerReference('1234#action', callServer);

The technique I use here is that the compiled output has to call createServerReference(id, callServer) with the $$id and proxy implementation. We then return a proxy function that is registered with a WeakMap to the particular instance of the Flight Client.

This might be hard to implement because it requires emitting module imports to a specific stateful runtime module in the compiler. A benefit is that this ensures that this particular reference is locked to a specific client if there are multiple - e.g. talking to different servers.

It's fairly arbitrary whether we use a WeakMap technique (like we do on the client) vs an $$id (like we do on the server). Not sure what's best overall. The WeakMap is nice because it doesn't leak implementation details that might be abused to consumers. We should probably pick one and unify.

@sebmarkbage sebmarkbage requested review from gaearon and gnoff April 15, 2023 03:00
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Apr 15, 2023
@react-sizebot
Copy link

react-sizebot commented Apr 15, 2023

Comparing: da6c23a...1cc2994

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 = 164.42 kB 164.42 kB = 51.69 kB 51.69 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 166.86 kB 166.86 kB = 52.34 kB 52.34 kB
facebook-www/ReactDOM-prod.classic.js = 564.45 kB 564.45 kB = 99.40 kB 99.40 kB
facebook-www/ReactDOM-prod.modern.js = 548.24 kB 548.24 kB = 96.71 kB 96.71 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-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.min.js +1.67% 9.09 kB 9.24 kB +1.14% 3.60 kB 3.65 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.min.js +1.67% 9.09 kB 9.24 kB +1.14% 3.60 kB 3.65 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.min.js +1.67% 9.09 kB 9.24 kB +1.14% 3.60 kB 3.65 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.production.min.js +1.62% 9.33 kB 9.48 kB +1.10% 3.71 kB 3.75 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.production.min.js +1.62% 9.33 kB 9.48 kB +1.10% 3.71 kB 3.75 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.production.min.js +1.62% 9.33 kB 9.48 kB +1.10% 3.71 kB 3.75 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.development.js +0.79% 47.72 kB 48.10 kB +0.39% 11.25 kB 11.30 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.development.js +0.79% 47.72 kB 48.10 kB +0.39% 11.25 kB 11.30 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-client.browser.development.js +0.79% 47.72 kB 48.10 kB +0.39% 11.25 kB 11.30 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +0.78% 44.87 kB 45.22 kB +0.37% 11.07 kB 11.11 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +0.78% 44.87 kB 45.22 kB +0.37% 11.07 kB 11.11 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +0.78% 44.87 kB 45.22 kB +0.37% 11.07 kB 11.11 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.production.min.js +0.76% 6.73 kB 6.78 kB +0.63% 2.69 kB 2.70 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.production.min.js +0.76% 6.73 kB 6.78 kB +0.63% 2.69 kB 2.70 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.production.min.js +0.76% 6.73 kB 6.78 kB +0.63% 2.69 kB 2.70 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.production.min.js +0.72% 7.11 kB 7.16 kB +0.59% 2.87 kB 2.89 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.production.min.js +0.72% 7.11 kB 7.16 kB +0.59% 2.87 kB 2.89 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.production.min.js +0.72% 7.11 kB 7.16 kB +0.59% 2.87 kB 2.89 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.production.min.js +0.71% 7.18 kB 7.23 kB +0.51% 2.92 kB 2.94 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.production.min.js +0.71% 7.18 kB 7.23 kB +0.51% 2.92 kB 2.94 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.production.min.js +0.71% 7.18 kB 7.23 kB +0.51% 2.92 kB 2.94 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +0.53% 24.64 kB 24.77 kB +0.37% 6.44 kB 6.47 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +0.53% 24.64 kB 24.77 kB +0.37% 6.44 kB 6.47 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +0.53% 24.64 kB 24.77 kB +0.37% 6.44 kB 6.47 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +0.50% 26.17 kB 26.30 kB +0.35% 6.89 kB 6.91 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +0.50% 26.17 kB 26.30 kB +0.35% 6.89 kB 6.91 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +0.50% 26.17 kB 26.30 kB +0.35% 6.89 kB 6.91 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +0.49% 26.38 kB 26.51 kB +0.30% 6.97 kB 7.00 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +0.49% 26.38 kB 26.51 kB +0.30% 6.97 kB 7.00 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +0.49% 26.38 kB 26.51 kB +0.30% 6.97 kB 7.00 kB

Generated by 🚫 dangerJS against 1cc2994

@dai-shi
Copy link
Contributor

dai-shi commented Apr 15, 2023

This lets the client bundle encode Server References without them first being passed from an RSC payload.

Oh, I assumed there were some design decisions only to allow passing server functions in props.

This lets the client bundle encode Server References without them first
being passed from an RSC payload. Like if you just import "use server"
from the client.

In the future we could expand this to allow .bind() too.
@sebmarkbage sebmarkbage merged commit b600620 into facebook:main Apr 15, 2023
unstubbable added a commit to unstubbable/mfng that referenced this pull request Apr 17, 2023
unstubbable added a commit to unstubbable/mfng that referenced this pull request Apr 17, 2023
kassens pushed a commit to kassens/react that referenced this pull request Apr 17, 2023
…26632)

This lets the client bundle encode Server References without them first
being passed from an RSC payload. Like if you just import `"use server"`
from the client. A bundler could already emit these proxies to be called
on the client but the subtle difference is that those proxies couldn't
be passed back into the server by reference. They have to be registered
with React.

We don't currently implement importing `"use server"` from client
components in the reference implementation. It'd need to expand the
Webpack plugin with a loader that rewrites files with the `"use server"`
in the client bundle.

```
"use server";

export async function action() {
   ...
}
```
->
```
import {createServerReference} from "react-server-dom-webpack/client";
import {callServer} from "some-router/call-server";

export const action = createServerReference('1234#action', callServer);
```

The technique I use here is that the compiled output has to call
`createServerReference(id, callServer)` with the `$$id` and proxy
implementation. We then return a proxy function that is registered with
a WeakMap to the particular instance of the Flight Client.

This might be hard to implement because it requires emitting module
imports to a specific stateful runtime module in the compiler. A benefit
is that this ensures that this particular reference is locked to a
specific client if there are multiple - e.g. talking to different
servers.

It's fairly arbitrary whether we use a WeakMap technique (like we do on
the client) vs an `$$id` (like we do on the server). Not sure what's
best overall. The WeakMap is nice because it doesn't leak implementation
details that might be abused to consumers. We should probably pick one
and unify.
unstubbable added a commit to unstubbable/mfng that referenced this pull request Apr 18, 2023
shuding added a commit to vercel/next.js that referenced this pull request Apr 27, 2023
Mostly mirrors the changed made in
facebook/react#26632 to our SWC transform. The
implementation difference is that the AST transformer only adds a
general purpose wrapper call `createServerReference(id)` from an aliased
import, so we can easily change the underlying function in the bundler.
This change only affects the client layer (when `self.config.is_server
=== false`).

Needs to be landed after another React upgrade:
#48697.

cc @sebmarkbage.
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…26632)

This lets the client bundle encode Server References without them first
being passed from an RSC payload. Like if you just import `"use server"`
from the client. A bundler could already emit these proxies to be called
on the client but the subtle difference is that those proxies couldn't
be passed back into the server by reference. They have to be registered
with React.

We don't currently implement importing `"use server"` from client
components in the reference implementation. It'd need to expand the
Webpack plugin with a loader that rewrites files with the `"use server"`
in the client bundle.

```
"use server";

export async function action() {
   ...
}
```
->
```
import {createServerReference} from "react-server-dom-webpack/client";
import {callServer} from "some-router/call-server";

export const action = createServerReference('1234#action', callServer);
```

The technique I use here is that the compiled output has to call
`createServerReference(id, callServer)` with the `$$id` and proxy
implementation. We then return a proxy function that is registered with
a WeakMap to the particular instance of the Flight Client.

This might be hard to implement because it requires emitting module
imports to a specific stateful runtime module in the compiler. A benefit
is that this ensures that this particular reference is locked to a
specific client if there are multiple - e.g. talking to different
servers.

It's fairly arbitrary whether we use a WeakMap technique (like we do on
the client) vs an `$$id` (like we do on the server). Not sure what's
best overall. The WeakMap is nice because it doesn't leak implementation
details that might be abused to consumers. We should probably pick one
and unify.
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
This lets the client bundle encode Server References without them first
being passed from an RSC payload. Like if you just import `"use server"`
from the client. A bundler could already emit these proxies to be called
on the client but the subtle difference is that those proxies couldn't
be passed back into the server by reference. They have to be registered
with React.

We don't currently implement importing `"use server"` from client
components in the reference implementation. It'd need to expand the
Webpack plugin with a loader that rewrites files with the `"use server"`
in the client bundle.

```
"use server";

export async function action() {
   ...
}
```
->
```
import {createServerReference} from "react-server-dom-webpack/client";
import {callServer} from "some-router/call-server";

export const action = createServerReference('1234#action', callServer);
```

The technique I use here is that the compiled output has to call
`createServerReference(id, callServer)` with the `$$id` and proxy
implementation. We then return a proxy function that is registered with
a WeakMap to the particular instance of the Flight Client.

This might be hard to implement because it requires emitting module
imports to a specific stateful runtime module in the compiler. A benefit
is that this ensures that this particular reference is locked to a
specific client if there are multiple - e.g. talking to different
servers.

It's fairly arbitrary whether we use a WeakMap technique (like we do on
the client) vs an `$$id` (like we do on the server). Not sure what's
best overall. The WeakMap is nice because it doesn't leak implementation
details that might be abused to consumers. We should probably pick one
and unify.

DiffTrain build for commit b600620.
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