@@ -516,6 +516,48 @@ function createLazyWrapperAroundWakeable(wakeable: Wakeable) {
516516 return lazyType ;
517517}
518518
519+ function renderFragment(
520+ request: Request,
521+ task: Task,
522+ children: $ReadOnlyArray< ReactClientValue > ,
523+ ): ReactJSONValue {
524+ if ( ! enableServerComponentKeys ) {
525+ return children ;
526+ }
527+ if (task.keyPath !== null) {
528+ // We have a Server Component that specifies a key but we're now splitting
529+ // the tree using a fragment.
530+ const fragment = [
531+ REACT_ELEMENT_TYPE ,
532+ REACT_FRAGMENT_TYPE ,
533+ task . keyPath ,
534+ { children} ,
535+ ] ;
536+ if ( ! task . implicitSlot ) {
537+ // If this was keyed inside a set. I.e. the outer Server Component was keyed
538+ // then we need to handle reorders of the whole set. To do this we need to wrap
539+ // this array in a keyed Fragment.
540+ return fragment ;
541+ }
542+ // If the outer Server Component was implicit but then an inner one had a key
543+ // we don't actually need to be able to move the whole set around. It'll always be
544+ // in an implicit slot. The key only exists to be able to reset the state of the
545+ // children. We could achieve the same effect by passing on the keyPath to the next
546+ // set of components inside the fragment. This would also allow a keyless fragment
547+ // reconcile against a single child.
548+ // Unfortunately because of JSON.stringify, we can't call the recursive loop for
549+ // each child within this context because we can't return a set with already resolved
550+ // values. E.g. a string would get double encoded. Returning would pop the context.
551+ // So instead, we wrap it with an unkeyed fragment and inner keyed fragment.
552+ return [ fragment ] ;
553+ }
554+ // Since we're yielding here, that implicitly resets the keyPath context on the
555+ // way up. Which is what we want since we've consumed it. If this changes to
556+ // be recursive serialization, we need to reset the keyPath and implicitSlot,
557+ // before recursing here.
558+ return children;
559+ }
560+
519561function renderClientElement (
520562 task : Task ,
521563 type : any ,
@@ -638,6 +680,7 @@ function renderElement(
638680 props.children,
639681 );
640682 task.implicitSlot = prevImplicitSlot;
683+ return json;
641684 }
642685 // This might be a built-in React component. We'll let the client decide.
643686 // Any built-in works as long as its props are serializable.
@@ -1334,8 +1377,7 @@ function renderModelDestructive(
13341377 }
13351378
13361379 if (isArray(value)) {
1337- // $FlowFixMe[incompatible-return]
1338- return value ;
1380+ return renderFragment ( request , task , value ) ;
13391381 }
13401382
13411383 if (value instanceof Map) {
@@ -1401,7 +1443,7 @@ function renderModelDestructive(
14011443
14021444 const iteratorFn = getIteratorFn ( value ) ;
14031445 if ( iteratorFn ) {
1404- return Array . from ( ( value : any ) ) ;
1446+ return renderFragment ( request , task , Array . from ( ( value : any ) ) ) ;
14051447 }
14061448
14071449 // Verify that this is a simple plain object.
0 commit comments