Skip to content

Commit eb262de

Browse files
refactor: enhance error handling and improve code clarity in server component rendering and tests
1 parent 0e4b561 commit eb262de

File tree

7 files changed

+29
-40
lines changed

7 files changed

+29
-40
lines changed

jest.config.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ export default {
1414
}),
1515
testEnvironment: 'jsdom',
1616
setupFiles: ['<rootDir>/node_package/tests/jest.setup.js'],
17-
// React Server Components tests are compatible with React 19
18-
// That only run with node version 18 and above
17+
// React Server Components tests require React 19 and only run with Node version 18 (`newest` in our CI matrix)
1918
moduleNameMapper:
2019
nodeVersion < 18
2120
? {

node_package/src/injectRSCPayload.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { PipeableStream } from 'react-dom/server';
22
import { PassThrough, Transform } from 'stream';
3+
import { finished } from 'stream/promises';
34
import { RailsContextWithServerComponentCapabilities } from './types/index.ts';
45

56
// In JavaScript, when an escape sequence with a backslash (\) is followed by a character
@@ -90,15 +91,7 @@ export default function injectRSCPayload(
9091
);
9192
});
9293

93-
await new Promise((resolve) => {
94-
if (htmlStream.readableEnded) {
95-
resolve(Promise.all(rscPromises));
96-
} else {
97-
htmlStream.on('end', () => {
98-
resolve(Promise.all(rscPromises));
99-
});
100-
}
101-
});
94+
await finished(htmlStream).then(() => Promise.all(rscPromises));
10295
} catch (err) {
10396
resultStream.emit('error', err);
10497
}

node_package/src/registerServerComponent/server.rsc.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import { ReactComponent, RenderFunction } from '../types/index.ts';
1414
* @example
1515
* ```js
1616
* registerServerComponent({
17-
* ServerComponent1: ServerComponent1Component,
18-
* ServerComponent2: ServerComponent2Component
17+
* ServerComponent1,
18+
* ServerComponent2,
1919
* });
2020
* ```
2121
*/

node_package/src/wrapServerComponentRenderer/client.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,28 +45,29 @@ const wrapServerComponentRenderer = (componentOrRenderFunction: ReactComponentOr
4545
throw new Error('wrapServerComponentRenderer: component is not a function');
4646
}
4747

48+
if (!domNodeId) {
49+
throw new Error('RSCClientRoot: No domNodeId provided');
50+
}
51+
const domNode = document.getElementById(domNodeId);
52+
if (!domNode) {
53+
throw new Error(`RSCClientRoot: No DOM node found for id: ${domNodeId}`);
54+
}
55+
4856
assertRailsContextWithComponentSpecificMetadata(railsContext);
4957

5058
const RSCProvider = createRSCProvider({
5159
railsContext,
5260
getServerComponent: getReactServerComponent,
5361
});
5462

55-
const SuspensableRSCRoute = (
56-
<React.Suspense fallback={null}>
57-
<Component {...props} />
58-
</React.Suspense>
63+
const root = (
64+
<RSCProvider>
65+
<React.Suspense fallback={null}>
66+
<Component {...props} />
67+
</React.Suspense>
68+
</RSCProvider>
5969
);
6070

61-
const root = <RSCProvider>{SuspensableRSCRoute}</RSCProvider>;
62-
63-
if (!domNodeId) {
64-
throw new Error('RSCClientRoot: No domNodeId provided');
65-
}
66-
const domNode = document.getElementById(domNodeId);
67-
if (!domNode) {
68-
throw new Error(`RSCClientRoot: No DOM node found for id: ${domNodeId}`);
69-
}
7071
if (domNode.innerHTML) {
7172
ReactDOMClient.hydrateRoot(domNode, root, { identifierPrefix: domNodeId });
7273
} else {

node_package/src/wrapServerComponentRenderer/server.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,13 @@ const wrapServerComponentRenderer = (componentOrRenderFunction: ReactComponentOr
4545
getServerComponent: getReactServerComponent,
4646
});
4747

48-
const suspensableServerComponent = (
49-
<React.Suspense fallback={null}>
50-
<Component {...props} />
51-
</React.Suspense>
48+
return () => (
49+
<RSCProvider>
50+
<React.Suspense fallback={null}>
51+
<Component {...props} />
52+
</React.Suspense>
53+
</RSCProvider>
5254
);
53-
54-
const root = <RSCProvider>{suspensableServerComponent}</RSCProvider>;
55-
56-
return () => root;
5755
};
5856

5957
return wrapper;

node_package/tests/SuspenseHydration.test.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { getNodeVersion } from './testUtils.js';
2121
* RSC payload to be present in the page before client-side hydration can occur.
2222
* And because the RSCRoute is rendered on the server because it's being hydrated on the client,
2323
* we can sure that the RSC payload is present in the page before the RSCRoute is hydrated on the client.
24-
* That's because `getRSCPayloadStream` function embeds the RSC payload immediately to the html stream even before the RSCRoute is rendered on the server.
24+
* That's because `getRSCPayloadStream` function embeds the RSC payload immediately to the HTML stream even before the RSCRoute is rendered on the server.
2525
* Without this guarantee, hydration would fail or produce incorrect results.
2626
*/
2727

@@ -152,8 +152,7 @@ async function renderAndHydrate() {
152152
};
153153
}
154154

155-
// React Server Components tests are compatible with React 19
156-
// That only run with node version 18 and above
155+
// React Server Components tests require React 19 and only run with Node version 18 (`newest` in our CI matrix)
157156
(getNodeVersion() >= 18 ? describe : describe.skip)('RSCClientRoot', () => {
158157
beforeEach(() => {
159158
jest.clearAllMocks();

node_package/tests/registerServerComponent.client.test.jsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ import { clear as clearComponentRegistry } from '../src/ComponentRegistry.ts';
1616

1717
enableFetchMocks();
1818

19-
// React Server Components tests are compatible with React 19
20-
// That only run with node version 18 and above
19+
// React Server Components tests require React 19 and only run with Node version 18 (`newest` in our CI matrix)
2120
(getNodeVersion() >= 18 ? describe : describe.skip)('RSCClientRoot', () => {
2221
let container;
2322
const mockDomNodeId = 'test-container';
@@ -242,7 +241,7 @@ enableFetchMocks();
242241
document.dispatchEvent(new Event('DOMContentLoaded'));
243242
});
244243

245-
// After second chunk, AsyncComponent should now be visible and loading indicator gone
244+
// After the second chunk, AsyncComponent should now be visible and loading indicator gone
246245
expect(screen.getByText('AsyncComponent')).toBeInTheDocument();
247246
expect(screen.queryByText('Loading AsyncComponent...')).not.toBeInTheDocument();
248247

0 commit comments

Comments
 (0)