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

3 files changed

Lines changed: 47 additions & 5 deletions

File tree

‎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
throwchunk.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
constid=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+
functionUndefined(){
202+
returnundefined;
203+
}
204+
205+
constmodel=<Undefined/>;
206+
207+
consttransport=ReactNoopFlightServer.render(model);
208+
209+
awaitact(async()=>{
210+
ReactNoop.render(awaitReactNoopFlightClient.read(transport));
211+
});
212+
213+
expect(ReactNoop).toMatchRenderedOutput(null);
214+
});
215+
216+
it('can render an empty fragment',async()=>{
217+
functionEmpty(){
218+
return<React.Fragment/>;
219+
}
220+
221+
constmodel=<Empty/>;
222+
223+
consttransport=ReactNoopFlightServer.render(model);
224+
225+
awaitact(async()=>{
226+
ReactNoop.render(awaitReactNoopFlightClient.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
functionSharedComponent({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+
functionserializeUndefined(): string{
551+
return'$undefined';
552+
}
553+
549554
functionserializeClientReference(
550555
request: Request,
551556
parent:
@@ -1134,14 +1139,14 @@ export function resolveModelToJSON(
11341139
returnescapeStringValue(value);
11351140
}
11361141

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

1146+
if(typeofvalue==='undefined'){
1147+
returnserializeUndefined();
1148+
}
1149+
11451150
if(typeofvalue==='function'){
11461151
if(isClientReference(value)){
11471152
returnserializeClientReference(request,parent,key,(value: any));

0 commit comments

Comments
 (0)