Skip to content

Commit 7de4f6f

Browse files
committed
Add server actions to client rendered RSC example
1 parent 548c70b commit 7de4f6f

File tree

9 files changed

+66
-24
lines changed

9 files changed

+66
-24
lines changed

packages/examples/rsc-client/client/App.tsx

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {ReactElement, Suspense} from 'react';
2-
import {fetchRSC} from '@parcel/rsc/client';
2+
import {fetchRSC, setServerCallback} from '@parcel/rsc/client';
33

44
export function App() {
55
return (
@@ -19,3 +19,14 @@ function RSC() {
1919
request ??= fetchRSC('http://localhost:3001');
2020
return request;
2121
}
22+
23+
setServerCallback(async (id, args) => {
24+
let result = await fetchRSC('http://localhost:3001/action', {
25+
method: 'POST',
26+
headers: {
27+
'rsc-action-id': id,
28+
},
29+
body: args,
30+
});
31+
return result;
32+
});

packages/examples/rsc-client/package.json

+2-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@
1111
},
1212
"server": {
1313
"source": "server/server.tsx",
14-
"context": "react-server",
15-
"includeNodeModules": {
16-
"express": false
17-
}
14+
"context": "react-server"
1815
}
1916
},
2017
"scripts": {
@@ -24,6 +21,7 @@
2421
"dependencies": {
2522
"@parcel/rsc": "*",
2623
"express": "^4.18.2",
24+
"cors": "^2.8.5",
2725
"react": "^19",
2826
"react-dom": "^19"
2927
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
"use client";
22

3-
import { useState } from "react";
3+
import { useState, startTransition } from "react";
4+
import {like} from './actions';
45

5-
export function Counter() {
6-
let [count, setCount] = useState(0);
7-
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
6+
export function Counter({likes}) {
7+
let [count, setCount] = useState(likes);
8+
let onClick = () => {
9+
startTransition(async () => {
10+
let newLikeCount = await like();
11+
setCount(newLikeCount);
12+
});
13+
};
14+
15+
return <button onClick={onClick}>Count: {count}</button>;
816
}
+2-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
"use server-entry";
22

33
import {Counter} from './Counter';
4+
import {likeCount} from './actions';
45
import './RSC.css';
56

67
export async function RSC() {
78
return (
89
<div className="rsc">
910
<h2>RSC!</h2>
10-
<Counter />
11+
<Counter likes={likeCount} />
1112
</div>
1213
);
1314
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use server';
2+
3+
export let likeCount = 0;
4+
5+
export async function like() {
6+
likeCount++;
7+
return likeCount;
8+
}
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
// Server dependencies.
22
import express from 'express';
3-
import {renderRSC} from '@parcel/rsc/node';
3+
import cors from 'cors';
4+
import {renderRSC, callAction} from '@parcel/rsc/node';
45

56
// Page components. These must have "use server-entry" so they are treated as code splitting entry points.
67
import {RSC} from './RSC';
78

89
const app = express();
9-
10-
app.use(function (req, res, next) {
11-
res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD,POST');
12-
res.setHeader('Access-Control-Allow-Origin', '*');
13-
res.setHeader('Access-Control-Allow-Headers', 'rsc-action');
14-
next();
15-
});
10+
app.use(cors());
1611

1712
app.get('/', async (req, res) => {
1813
// Render the server component to an RSC payload.
@@ -22,5 +17,13 @@ app.get('/', async (req, res) => {
2217
stream.pipe(res);
2318
});
2419

20+
app.post('/action', async (req, res) => {
21+
let id = req.get('rsc-action-id');
22+
let {result} = await callAction(req, id);
23+
let stream = renderRSC(result);
24+
res.set('Content-Type', 'text/x-component');
25+
stream.pipe(res);
26+
});
27+
2528
app.listen(3001);
2629
console.log('Server listening on port 3001');

packages/transformers/js/src/JSTransformer.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ export default (new Transformer({
776776
sourceType: 'module',
777777
outputFormat: 'commonjs',
778778
engines: asset.env.engines,
779-
includeNodeModules: false,
779+
includeNodeModules: true,
780780
isLibrary: false,
781781
sourceMap: asset.env.sourceMap,
782782
shouldOptimize: asset.env.shouldOptimize,

packages/utils/rsc/src/client.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
/* @jsxRuntime automatic */
2-
import { ReactNode, startTransition, useInsertionEffect } from 'react';
3-
import {createFromReadableStream, createFromFetch, encodeReply, setServerCallback, createTemporaryReferenceSet} from 'react-server-dom-parcel/client';
2+
import {ReactNode, startTransition, useInsertionEffect} from 'react';
3+
import {createFromReadableStream, createFromFetch, encodeReply, setServerCallback as setReactServerCallback, createTemporaryReferenceSet} from 'react-server-dom-parcel/client';
44
import {rscStream} from 'rsc-html-stream/client';
5-
import { hydrateRoot, HydrationOptions, Root } from 'react-dom/client';
5+
import {hydrateRoot, HydrationOptions, Root} from 'react-dom/client';
6+
7+
type CallServerCallback = <T>(id: string, args: any[]) => Promise<T>;
8+
export function setServerCallback(cb: CallServerCallback): void {
9+
return setReactServerCallback(cb);
10+
}
611

712
// Stream in initial RSC payload embedded in the HTML.
813
let initialRSCPayload: Promise<ReactNode>;

yarn.lock

+10-2
Original file line numberDiff line numberDiff line change
@@ -4750,6 +4750,14 @@ core-util-is@~1.0.0:
47504750
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
47514751
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
47524752

4753+
cors@^2.8.5:
4754+
version "2.8.5"
4755+
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
4756+
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
4757+
dependencies:
4758+
object-assign "^4"
4759+
vary "^1"
4760+
47534761
47544762
version "7.0.0"
47554763
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3"
@@ -10433,7 +10441,7 @@ [email protected], "nx@>=15.5.2 < 16":
1043310441
"@nrwl/nx-win32-arm64-msvc" "15.9.7"
1043410442
"@nrwl/nx-win32-x64-msvc" "15.9.7"
1043510443

10436-
object-assign@^4.0.1, object-assign@^4.1.1:
10444+
object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.1:
1043710445
version "4.1.1"
1043810446
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
1043910447
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
@@ -14473,7 +14481,7 @@ value-or-function@^3.0.0:
1447314481
resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813"
1447414482
integrity sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==
1447514483

14476-
vary@~1.1.2:
14484+
vary@^1, vary@~1.1.2:
1447714485
version "1.1.2"
1447814486
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
1447914487
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==

0 commit comments

Comments
 (0)