Skip to content

Commit f3e8681

Browse files
committed
Tail recursively render the result of server components
That way this only happens in the non-terminal branches instead of being called on the terminal branches too.
1 parent eabf7e4 commit f3e8681

File tree

1 file changed

+27
-16
lines changed

1 file changed

+27
-16
lines changed

packages/react-server/src/ReactFlightServer.js

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ type ReactJSONValue =
137137
| boolean
138138
| number
139139
| null
140-
| $ReadOnlyArray<ReactJSONValue>
140+
| $ReadOnlyArray<ReactClientValue>
141141
| ReactClientObject;
142142

143143
// Serializable values
@@ -502,12 +502,13 @@ function createLazyWrapperAroundWakeable(wakeable: Wakeable) {
502502

503503
function renderElement(
504504
request: Request,
505+
task: Task,
505506
type: any,
506507
key: null | React$Key,
507508
ref: mixed,
508509
props: any,
509510
prevThenableState: ThenableState | null,
510-
): ReactClientValue {
511+
): ReactJSONValue {
511512
if (ref !== null && ref !== undefined) {
512513
// When the ref moves to the regular props object this will implicitly
513514
// throw for functions. We could probably relax it to a DEV warning for other
@@ -529,7 +530,7 @@ function renderElement(
529530
}
530531
// This is a server-side component.
531532
prepareToUseHooksForComponent(prevThenableState);
532-
const result = type(props);
533+
let result = type(props);
533534
if (
534535
typeof result === 'object' &&
535536
result !== null &&
@@ -543,9 +544,9 @@ function renderElement(
543544
}
544545
// TODO: Once we accept Promises as children on the client, we can just return
545546
// the thenable here.
546-
return createLazyWrapperAroundWakeable(result);
547+
result = createLazyWrapperAroundWakeable(result);
547548
}
548-
return result;
549+
return renderModelDestructive(request, task, emptyRoot, '', result, null);
549550
} else if (typeof type === 'string') {
550551
// This is a host element. E.g. HTML.
551552
return [REACT_ELEMENT_TYPE, type, key, props];
@@ -555,7 +556,14 @@ function renderElement(
555556
// it as a wrapper.
556557
// TODO: If a key is specified, we should propagate its key to any children.
557558
// Same as if a Server Component has a key.
558-
return props.children;
559+
return renderModelDestructive(
560+
request,
561+
task,
562+
emptyRoot,
563+
'',
564+
props.children,
565+
null,
566+
);
559567
}
560568
// This might be a built-in React component. We'll let the client decide.
561569
// Any built-in works as long as its props are serializable.
@@ -572,6 +580,7 @@ function renderElement(
572580
const wrappedType = init(payload);
573581
return renderElement(
574582
request,
583+
task,
575584
wrappedType,
576585
key,
577586
ref,
@@ -582,11 +591,20 @@ function renderElement(
582591
case REACT_FORWARD_REF_TYPE: {
583592
const render = type.render;
584593
prepareToUseHooksForComponent(prevThenableState);
585-
return render(props, undefined);
594+
const result = render(props, undefined);
595+
return renderModelDestructive(
596+
request,
597+
task,
598+
emptyRoot,
599+
'',
600+
result,
601+
null,
602+
);
586603
}
587604
case REACT_MEMO_TYPE: {
588605
return renderElement(
589606
request,
607+
task,
590608
type.type,
591609
key,
592610
ref,
@@ -1105,22 +1123,15 @@ function renderModelDestructive(
11051123
// TODO: Concatenate keys of parents onto children.
11061124
const element: React$Element<any> = (value: any);
11071125
// Attempt to render the Server Component.
1108-
const result = renderElement(
1126+
return renderElement(
11091127
request,
1128+
task,
11101129
element.type,
11111130
element.key,
11121131
element.ref,
11131132
element.props,
11141133
prevThenableState,
11151134
);
1116-
return renderModelDestructive(
1117-
request,
1118-
task,
1119-
emptyRoot,
1120-
'',
1121-
result,
1122-
null,
1123-
);
11241135
}
11251136
case REACT_LAZY_TYPE: {
11261137
const payload = (value: any)._payload;

0 commit comments

Comments
 (0)