@@ -641,8 +641,10 @@ class ReactDOMServerRenderer {
641
641
previousWasTextNode: boolean;
642
642
makeStaticMarkup: boolean;
643
643
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
646
648
647
649
constructor ( children : mixed , makeStaticMarkup : boolean ) {
648
650
const flatChildren = flattenTopLevelChildren ( children ) ;
@@ -667,46 +669,65 @@ class ReactDOMServerRenderer {
667
669
this . makeStaticMarkup = makeStaticMarkup ;
668
670
669
671
// 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
+ }
672
678
}
673
679
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
+
674
690
pushProvider < T > ( provider : ReactProvider < T > ) : void {
675
- this. providerIndex += 1 ;
676
- this . providerStack [ this . providerIndex ] = provider ;
691
+ const index = ++ this . contextIndex ;
677
692
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.
678
704
context . _currentValue = provider . props . value ;
679
705
}
680
706
681
707
popProvider < T > ( provider : ReactProvider < T > ) : void {
708
+ const index = this . contextIndex ;
682
709
if ( __DEV__ ) {
683
710
warning (
684
- this . providerIndex > - 1 &&
685
- provider === this . providerStack [ this . providerIndex ] ,
711
+ index > - 1 && provider === ( this . contextProviderStack : any ) [ index ] ,
686
712
'Unexpected pop.' ,
687
713
) ;
688
714
}
689
- this.providerStack[this.providerIndex] = null;
690
- this.providerIndex -= 1;
691
- const context: ReactContext< any > = provider.type._context;
692
715
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 ) ;
709
726
}
727
+ this . contextIndex -- ;
728
+
729
+ // Restore to the previous value we stored as we were walking down.
730
+ context . _currentValue = previousValue ;
710
731
}
711
732
712
733
read ( bytes : number ) : string | null {
0 commit comments