Skip to content

Commit d1ad984

Browse files
authored
[Flight] Add support for returning undefined from render (#26349)
## Summary Adds support for returning `undefined` from Server Components. Also fixes a bug where rendering an empty fragment would throw the same error as returning undefined. ## How did you test this change? - [x] test failed with same error message I got when returning undefined from Server Components in a Next.js app - [x] test passes after adding encoding for `undefined`
1 parent 39d4b93 commit d1ad984

File tree

3 files changed

+47
-5
lines changed

3 files changed

+47
-5
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,11 @@ export function parseModelString(
556556
throw chunk.reason;
557557
}
558558
}
559+
case 'u': {
560+
// matches "$undefined"
561+
// Special encoding for `undefined` which can't be serialized as JSON otherwise.
562+
return undefined;
563+
}
559564
default: {
560565
// We assume that anything else is a reference ID.
561566
const id = parseInt(value.substring(1), 16);

packages/react-client/src/__tests__/ReactFlight-test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,38 @@ describe('ReactFlight', () => {
197197
expect(ReactNoop).toMatchRenderedOutput(<span>ABC</span>);
198198
});
199199

200+
it('can render undefined', async () => {
201+
function Undefined() {
202+
return undefined;
203+
}
204+
205+
const model = <Undefined />;
206+
207+
const transport = ReactNoopFlightServer.render(model);
208+
209+
await act(async () => {
210+
ReactNoop.render(await ReactNoopFlightClient.read(transport));
211+
});
212+
213+
expect(ReactNoop).toMatchRenderedOutput(null);
214+
});
215+
216+
it('can render an empty fragment', async () => {
217+
function Empty() {
218+
return <React.Fragment />;
219+
}
220+
221+
const model = <Empty />;
222+
223+
const transport = ReactNoopFlightServer.render(model);
224+
225+
await act(async () => {
226+
ReactNoop.render(await ReactNoopFlightClient.read(transport));
227+
});
228+
229+
expect(ReactNoop).toMatchRenderedOutput(null);
230+
});
231+
200232
it('can render a lazy component as a shared component on the server', async () => {
201233
function SharedComponent({text}) {
202234
return (

packages/react-server/src/ReactFlightServer.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export type ReactClientValue =
117117
| number
118118
| symbol
119119
| null
120+
| void
120121
| Iterable<ReactClientValue>
121122
| Array<ReactClientValue>
122123
| ReactClientObject
@@ -546,6 +547,10 @@ function serializeProviderReference(name: string): string {
546547
return '$P' + name;
547548
}
548549

550+
function serializeUndefined(): string {
551+
return '$undefined';
552+
}
553+
549554
function serializeClientReference(
550555
request: Request,
551556
parent:
@@ -1134,14 +1139,14 @@ export function resolveModelToJSON(
11341139
return escapeStringValue(value);
11351140
}
11361141

1137-
if (
1138-
typeof value === 'boolean' ||
1139-
typeof value === 'number' ||
1140-
typeof value === 'undefined'
1141-
) {
1142+
if (typeof value === 'boolean' || typeof value === 'number') {
11421143
return value;
11431144
}
11441145

1146+
if (typeof value === 'undefined') {
1147+
return serializeUndefined();
1148+
}
1149+
11451150
if (typeof value === 'function') {
11461151
if (isClientReference(value)) {
11471152
return serializeClientReference(request, parent, key, (value: any));

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy