@@ -62,6 +62,12 @@ import {
6262 REACT_PORTAL_TYPE ,
6363 REACT_LAZY_TYPE ,
6464 REACT_SUSPENSE_TYPE ,
65+ REACT_LEGACY_HIDDEN_TYPE ,
66+ REACT_DEBUG_TRACING_MODE_TYPE ,
67+ REACT_STRICT_MODE_TYPE ,
68+ REACT_PROFILER_TYPE ,
69+ REACT_SUSPENSE_LIST_TYPE ,
70+ REACT_FRAGMENT_TYPE ,
6571} from 'shared/ReactSymbols' ;
6672import ReactSharedInternals from 'shared/ReactSharedInternals' ;
6773import {
@@ -521,6 +527,8 @@ const didWarnAboutContextTypeOnFunctionComponent = {};
521527const didWarnAboutGetDerivedStateOnFunctionComponent = { } ;
522528let didWarnAboutReassigningProps = false ;
523529const didWarnAboutDefaultPropsOnFunctionComponent = { } ;
530+ let didWarnAboutGenerators = false ;
531+ let didWarnAboutMaps = false ;
524532
525533// This would typically be a function component but we still support module pattern
526534// components for some reason.
@@ -701,10 +709,67 @@ function renderElement(
701709 }
702710 } else if ( typeof type === 'string' ) {
703711 renderHostElement ( request , task , type , props ) ;
704- } else if ( type === REACT_SUSPENSE_TYPE ) {
705- renderSuspenseBoundary ( request , task , props ) ;
706712 } else {
707- throw new Error ( 'Not yet implemented element type.' ) ;
713+ switch ( type ) {
714+ // TODO: LegacyHidden acts the same as a fragment. This only works
715+ // because we currently assume that every instance of LegacyHidden is
716+ // accompanied by a host component wrapper. In the hidden mode, the host
717+ // component is given a `hidden` attribute, which ensures that the
718+ // initial HTML is not visible. To support the use of LegacyHidden as a
719+ // true fragment, without an extra DOM node, we would have to hide the
720+ // initial HTML in some other way.
721+ // TODO: Add REACT_OFFSCREEN_TYPE here too with the same capability.
722+ case REACT_LEGACY_HIDDEN_TYPE :
723+ case REACT_DEBUG_TRACING_MODE_TYPE :
724+ case REACT_STRICT_MODE_TYPE :
725+ case REACT_PROFILER_TYPE :
726+ case REACT_SUSPENSE_LIST_TYPE : // TODO: SuspenseList should control the boundaries.
727+ case REACT_FRAGMENT_TYPE : {
728+ renderNodeDestructive ( request , task , props . children ) ;
729+ break ;
730+ }
731+ case REACT_SUSPENSE_TYPE : {
732+ renderSuspenseBoundary ( request , task , props ) ;
733+ break ;
734+ }
735+ default : {
736+ throw new Error ( 'Not yet implemented element type.' ) ;
737+ }
738+ }
739+ }
740+ }
741+
742+ function validateIterable ( iterable , iteratorFn : Function ) : void {
743+ if ( __DEV__ ) {
744+ // We don't support rendering Generators because it's a mutation.
745+ // See https://github.com/facebook/react/issues/12995
746+ if (
747+ typeof Symbol === 'function' &&
748+ // $FlowFixMe Flow doesn't know about toStringTag
749+ iterable [ Symbol . toStringTag ] === 'Generator'
750+ ) {
751+ if ( ! didWarnAboutGenerators ) {
752+ console . error (
753+ 'Using Generators as children is unsupported and will likely yield ' +
754+ 'unexpected results because enumerating a generator mutates it. ' +
755+ 'You may convert it to an array with `Array.from()` or the ' +
756+ '`[...spread]` operator before rendering. Keep in mind ' +
757+ 'you might need to polyfill these features for older browsers.' ,
758+ ) ;
759+ }
760+ didWarnAboutGenerators = true ;
761+ }
762+
763+ // Warn about using Maps as children
764+ if ( ( iterable : any ) . entries === iteratorFn ) {
765+ if ( ! didWarnAboutMaps ) {
766+ console . error (
767+ 'Using Maps as children is not supported. ' +
768+ 'Use an array of keyed ReactElements instead.' ,
769+ ) ;
770+ }
771+ didWarnAboutMaps = true ;
772+ }
708773 }
709774}
710775
@@ -756,7 +821,30 @@ function renderNodeDestructive(
756821
757822 const iteratorFn = getIteratorFn ( node ) ;
758823 if ( iteratorFn ) {
759- throw new Error ( 'Not yet implemented node type.' ) ;
824+ if ( __DEV__ ) {
825+ validateIterable ( node , iteratorFn ( ) ) ;
826+ }
827+ const iterator = iteratorFn . call ( node ) ;
828+ if ( iterator ) {
829+ let step = iterator . next ( ) ;
830+ // If there are not entries, we need to push an empty so we start by checking that.
831+ if ( ! step . done ) {
832+ do {
833+ // Recursively render the rest. We need to use the non-destructive form
834+ // so that we can safely pop back up and render the sibling if something
835+ // suspends.
836+ renderNode ( request , task , step . value ) ;
837+ step = iterator . next ( ) ;
838+ } while ( ! step . done ) ;
839+ return ;
840+ }
841+ }
842+ pushEmpty (
843+ task . blockedSegment . chunks ,
844+ request . responseState ,
845+ task . assignID ,
846+ ) ;
847+ task . assignID = null ;
760848 }
761849
762850 const childString = Object . prototype . toString . call ( node ) ;
@@ -805,6 +893,7 @@ function renderNodeDestructive(
805893
806894 // Any other type is assumed to be empty.
807895 pushEmpty ( task . blockedSegment . chunks , request . responseState , task . assignID ) ;
896+ task . assignID = null ;
808897}
809898
810899function spawnNewSuspendedTask (
0 commit comments