@@ -641,8 +641,10 @@ class ReactDOMServerRenderer {
641641 previousWasTextNode: boolean;
642642 makeStaticMarkup: boolean;
643643
644- providerStack: Array< ?ReactProvider < any > > ;
645- providerIndex: number ;
644+ contextIndex: number;
645+ contextStack: Array< ReactContext < any > > ;
646+ contextValueStack: Array < any > ;
647+ contextProviderStack: ?Array < ReactProvider < any >> ; // DEV-only
646648
647649 constructor ( children : mixed , makeStaticMarkup : boolean ) {
648650 const flatChildren = flattenTopLevelChildren ( children ) ;
@@ -667,46 +669,65 @@ class ReactDOMServerRenderer {
667669 this . makeStaticMarkup = makeStaticMarkup ;
668670
669671 // Context (new API)
670- this . providerStack = [ ] ; // Stack of provider objects
671- this . providerIndex = - 1 ;
672+ this . contextIndex = - 1 ;
673+ this . contextStack = [ ] ;
674+ this . contextValueStack = [ ] ;
675+ if ( __DEV__ ) {
676+ this . contextProviderStack = [ ] ;
677+ }
672678 }
673679
680+ /**
681+ * Note: We use just two stacks regardless of how many context providers you have.
682+ * Providers are always popped in the reverse order to how they were pushed
683+ * so we always know on the way down which provider you'll encounter next on the way up.
684+ * On the way down, we push the current provider, and its context value *before*
685+ * we mutated it, onto the stacks. Therefore, on the way up, we always know which
686+ * provider needs to be "restored" to which value.
687+ * https://github.com/facebook/react/pull/12985#issuecomment-396301248
688+ */
689+
674690 pushProvider < T > ( provider : ReactProvider < T > ) : void {
675- this. providerIndex += 1 ;
676- this . providerStack [ this . providerIndex ] = provider ;
691+ const index = ++ this . contextIndex ;
677692 const context : ReactContext < any > = provider . type . _context ;
693+ const previousValue = context . _currentValue ;
694+
695+ // Remember which value to restore this context to on our way up.
696+ this . contextStack [ index ] = context ;
697+ this . contextValueStack [ index ] = previousValue ;
698+ if ( __DEV__ ) {
699+ // Only used for push/pop mismatch warnings.
700+ ( this . contextProviderStack : any ) [ index ] = provider ;
701+ }
702+
703+ // Mutate the current value.
678704 context . _currentValue = provider . props . value ;
679705 }
680706
681707 popProvider < T > ( provider : ReactProvider < T > ) : void {
708+ const index = this . contextIndex ;
682709 if ( __DEV__ ) {
683710 warning (
684- this . providerIndex > - 1 &&
685- provider === this . providerStack [ this . providerIndex ] ,
711+ index > - 1 && provider === ( this . contextProviderStack : any ) [ index ] ,
686712 'Unexpected pop.' ,
687713 ) ;
688714 }
689- this.providerStack[this.providerIndex] = null;
690- this.providerIndex -= 1;
691- const context: ReactContext< any > = provider.type._context;
692715
693- // Find the closest parent provider of the same type and use its value.
694- // TODO: it would be nice to avoid this being O(N).
695- let contextPriorProvider = null;
696- for (let i = this.providerIndex; i >= 0 ; i -- ) {
697- // We assume this Flow type is correct because of the index check above
698- // and because pushProvider() enforces the correct type.
699- const priorProvider : ReactProvider < any > = ( this . providerStack [ i ] : any ) ;
700- if ( priorProvider . type === provider . type ) {
701- contextPriorProvider = priorProvider ;
702- break ;
703- }
704- }
705- if (contextPriorProvider !== null) {
706- context . _currentValue = contextPriorProvider . props . value ;
707- } else {
708- context . _currentValue = context . _defaultValue ;
716+ const context : ReactContext < any > = this . contextStack [ index ] ;
717+ const previousValue = this . contextValueStack [ index ] ;
718+
719+ // "Hide" these null assignments from Flow by using `any`
720+ // because conceptually they are deletions--as long as we
721+ // promise to never access values beyond `this.contextIndex`.
722+ this . contextStack [ index ] = ( null : any ) ;
723+ this . contextValueStack [ index ] = ( null : any ) ;
724+ if ( __DEV__ ) {
725+ ( this . contextProviderStack : any ) [ index ] = ( null : any ) ;
709726 }
727+ this . contextIndex -- ;
728+
729+ // Restore to the previous value we stored as we were walking down.
730+ context . _currentValue = previousValue ;
710731 }
711732
712733 read ( bytes : number ) : string | null {
0 commit comments