Skip to content

Commit 8da98fa

Browse files
committed
Unify plain Server Component and forwardRef under one function
1 parent 0d11563 commit 8da98fa

File tree

1 file changed

+54
-75
lines changed

1 file changed

+54
-75
lines changed

packages/react-server/src/ReactFlightServer.js

Lines changed: 54 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,58 @@ function createLazyWrapperAroundWakeable(wakeable: Wakeable) {
478478
return lazyType;
479479
}
480480

481+
function renderFunctionComponent<Props>(
482+
request: Request,
483+
task: Task,
484+
key: null | string,
485+
Component: (p: Props, arg: void) => any,
486+
props: Props,
487+
): ReactJSONValue {
488+
// Reset the task's thenable state before continuing, so that if a later
489+
// component suspends we can reuse the same task object. If the same
490+
// component suspends again, the thenable state will be restored.
491+
const prevThenableState = task.thenableState;
492+
task.thenableState = null;
493+
494+
prepareToUseHooksForComponent(prevThenableState);
495+
// The secondArg is always undefined in Server Components since refs error early.
496+
const secondArg = undefined;
497+
let result = Component(props, secondArg);
498+
if (
499+
typeof result === 'object' &&
500+
result !== null &&
501+
typeof result.then === 'function'
502+
) {
503+
// When the return value is in children position we can resolve it immediately,
504+
// to its value without a wrapper if it's synchronously available.
505+
const thenable: Thenable<any> = result;
506+
if (thenable.status === 'fulfilled') {
507+
return thenable.value;
508+
}
509+
// TODO: Once we accept Promises as children on the client, we can just return
510+
// the thenable here.
511+
result = createLazyWrapperAroundWakeable(result);
512+
}
513+
// Track this element's key on the Server Component on the keyPath context..
514+
const prevKeyPath = task.keyPath;
515+
const prevImplicitSlot = task.implicitSlot;
516+
if (key !== null) {
517+
// Append the key to the path. Technically a null key should really add the child
518+
// index. We don't do that to hold the payload small and implementation simple.
519+
task.keyPath = prevKeyPath === null ? key : prevKeyPath + ',' + key;
520+
} else if (prevKeyPath === null) {
521+
// This sequence of Server Components has no keys. This means that it was rendered
522+
// in a slot that needs to assign an implicit key. Even if children below have
523+
// explicit keys, they should not be used for the outer most key since it might
524+
// collide with other slots in that set.
525+
task.implicitSlot = true;
526+
}
527+
const json = renderModelDestructive(request, task, emptyRoot, '', result);
528+
task.keyPath = prevKeyPath;
529+
task.implicitSlot = prevImplicitSlot;
530+
return json;
531+
}
532+
481533
function renderFragment(
482534
request: Request,
483535
task: Task,
@@ -581,48 +633,7 @@ function renderElement(
581633
return renderClientElement(task, type, key, props);
582634
}
583635
// This is a server-side component.
584-
585-
// Reset the task's thenable state before continuing, so that if a later
586-
// component suspends we can reuse the same task object. If the same
587-
// component suspends again, the thenable state will be restored.
588-
const prevThenableState = task.thenableState;
589-
task.thenableState = null;
590-
591-
prepareToUseHooksForComponent(prevThenableState);
592-
let result = type(props);
593-
if (
594-
typeof result === 'object' &&
595-
result !== null &&
596-
typeof result.then === 'function'
597-
) {
598-
// When the return value is in children position we can resolve it immediately,
599-
// to its value without a wrapper if it's synchronously available.
600-
const thenable: Thenable<any> = result;
601-
if (thenable.status === 'fulfilled') {
602-
return thenable.value;
603-
}
604-
// TODO: Once we accept Promises as children on the client, we can just return
605-
// the thenable here.
606-
result = createLazyWrapperAroundWakeable(result);
607-
}
608-
// Track this element's key on the Server Component on the keyPath context..
609-
const prevKeyPath = task.keyPath;
610-
const prevImplicitSlot = task.implicitSlot;
611-
if (key !== null) {
612-
// Append the key to the path. Technically a null key should really add the child
613-
// index. We don't do that to hold the payload small and implementation simple.
614-
task.keyPath = prevKeyPath === null ? key : prevKeyPath + ',' + key;
615-
} else if (prevKeyPath === null) {
616-
// This sequence of Server Components has no keys. This means that it was rendered
617-
// in a slot that needs to assign an implicit key. Even if children below have
618-
// explicit keys, they should not be used for the outer most key since it might
619-
// collide with other slots in that set.
620-
task.implicitSlot = true;
621-
}
622-
const json = renderModelDestructive(request, task, emptyRoot, '', result);
623-
task.keyPath = prevKeyPath;
624-
task.implicitSlot = prevImplicitSlot;
625-
return json;
636+
return renderFunctionComponent(request, task, key, type, props);
626637
} else if (typeof type === 'string') {
627638
// This is a host element. E.g. HTML.
628639
return renderClientElement(task, type, key, props);
@@ -660,39 +671,7 @@ function renderElement(
660671
return renderElement(request, task, wrappedType, key, ref, props);
661672
}
662673
case REACT_FORWARD_REF_TYPE: {
663-
const render = type.render;
664-
665-
// Reset the task's thenable state before continuing, so that if a later
666-
// component suspends we can reuse the same task object. If the same
667-
// component suspends again, the thenable state will be restored.
668-
const prevThenableState = task.thenableState;
669-
task.thenableState = null;
670-
671-
prepareToUseHooksForComponent(prevThenableState);
672-
const result = render(props, undefined);
673-
const prevKeyPath = task.keyPath;
674-
const prevImplicitSlot = task.implicitSlot;
675-
if (key !== null) {
676-
// Append the key to the path. Technically a null key should really add the child
677-
// index. We don't do that to hold the payload small and implementation simple.
678-
task.keyPath = prevKeyPath === null ? key : prevKeyPath + ',' + key;
679-
} else if (prevKeyPath === null) {
680-
// This sequence of Server Components has no keys. This means that it was rendered
681-
// in a slot that needs to assign an implicit key. Even if children below have
682-
// explicit keys, they should not be used for the outer most key since it might
683-
// collide with other slots in that set.
684-
task.implicitSlot = true;
685-
}
686-
const json = renderModelDestructive(
687-
request,
688-
task,
689-
emptyRoot,
690-
'',
691-
result,
692-
);
693-
task.keyPath = prevKeyPath;
694-
task.implicitSlot = prevImplicitSlot;
695-
return json;
674+
return renderFunctionComponent(request, task, key, type.render, props);
696675
}
697676
case REACT_MEMO_TYPE: {
698677
return renderElement(request, task, type.type, key, ref, props);

0 commit comments

Comments
 (0)