8
8
*/
9
9
10
10
import type { HostConfig } from 'react-reconciler' ;
11
- import type {
12
- ReactProviderType ,
13
- ReactConsumerType ,
14
- ReactContext ,
15
- } from 'shared/ReactTypes' ;
11
+ import type { ReactProviderType , ReactContext } from 'shared/ReactTypes' ;
16
12
import type { Fiber } from 'react-reconciler/src/ReactFiber' ;
17
13
import type { HostContext } from './ReactFiberHostContext' ;
18
14
import type { HydrationContext } from './ReactFiberHydrationContext' ;
@@ -68,6 +64,7 @@ import {
68
64
} from './ReactFiberContext' ;
69
65
import { pushProvider } from './ReactFiberNewContext' ;
70
66
import { NoWork , Never } from './ReactFiberExpirationTime' ;
67
+ import MAX_SIGNED_32_BIT_INT from './maxSigned32BitInt' ;
71
68
72
69
let warnedAboutStatelessRefs ;
73
70
@@ -616,6 +613,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
616
613
function propagateContextChange < V > (
617
614
workInProgress: Fiber,
618
615
context: ReactContext< V > ,
616
+ changedBits: number,
619
617
renderExpirationTime: ExpirationTime,
620
618
): void {
621
619
if ( enableNewContextAPI ) {
@@ -626,7 +624,8 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
626
624
switch ( fiber . tag ) {
627
625
case ConsumerComponent:
628
626
// Check if the context matches.
629
- if ( fiber . type . context === context ) {
627
+ const bits = fiber . stateNode ;
628
+ if ( fiber . type === context && ( bits & changedBits ) !== 0 ) {
630
629
// Update the expiration time of all the ancestors, including
631
630
// the alternates.
632
631
let node = fiber ;
@@ -668,7 +667,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
668
667
break ;
669
668
case ProviderComponent :
670
669
// Don't scan deeper if this is a matching provider
671
- nextFiber = fiber . type === context ? null : fiber . child ;
670
+ nextFiber = fiber . type === workInProgress . type ? null : fiber . child ;
672
671
break ;
673
672
default:
674
673
// Traverse down.
@@ -724,12 +723,33 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
724
723
workInProgress.memoizedProps = newProps;
725
724
726
725
const newValue = newProps.value;
727
- const oldValue = oldProps !== null ? oldProps.value : null;
728
726
729
- // Use Object.is to compare the new context value to the old value.
730
- if (!is(newValue, oldValue)) {
731
- propagateContextChange ( workInProgress , context , renderExpirationTime ) ;
727
+ let changedBits: number;
728
+ if (oldProps === null) {
729
+ // Initial render
730
+ changedBits = MAX_SIGNED_32_BIT_INT ;
731
+ } else {
732
+ const oldValue = oldProps . value ;
733
+ // Use Object.is to compare the new context value to the old value.
734
+ if ( ! is ( newValue , oldValue ) ) {
735
+ changedBits =
736
+ context . calculateChangedBits !== null
737
+ ? context . calculateChangedBits ( oldValue , newValue )
738
+ : MAX_SIGNED_32_BIT_INT ;
739
+ if ( changedBits !== 0 ) {
740
+ propagateContextChange (
741
+ workInProgress ,
742
+ context ,
743
+ changedBits ,
744
+ renderExpirationTime ,
745
+ ) ;
746
+ }
747
+ } else {
748
+ // No change.
749
+ changedBits = 0 ;
750
+ }
732
751
}
752
+ workInProgress . stateNode = changedBits ;
733
753
734
754
if ( oldProps !== null && oldProps . children === newProps . children ) {
735
755
return bailoutOnAlreadyFinishedWork ( current , workInProgress ) ;
@@ -748,8 +768,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
748
768
renderExpirationTime ,
749
769
) {
750
770
if ( enableNewContextAPI ) {
751
- const consumerType : ReactConsumerType < any > = workInProgress . type ;
752
- const context : ReactContext < any > = consumerType . context ;
771
+ const context : ReactContext < any > = workInProgress . type ;
753
772
754
773
const newProps = workInProgress . pendingProps ;
755
774
const oldProps = workInProgress . memoizedProps ;
@@ -758,12 +777,12 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
758
777
const providerFiber : Fiber | null = context . currentProvider ;
759
778
760
779
let newValue ;
761
- let valueDidChange ;
780
+ let changedBits ;
762
781
if ( providerFiber === null ) {
763
782
// This is a detached consumer (has no provider). Use the default
764
783
// context value.
765
784
newValue = context . defaultValue ;
766
- valueDidChange = false ;
785
+ changedBits = 0 ;
767
786
} else {
768
787
const provider = providerFiber . pendingProps ;
769
788
invariant (
@@ -772,35 +791,31 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
772
791
'a bug in React. Please file an issue.' ,
773
792
) ;
774
793
newValue = provider . value ;
775
-
776
- // Context change propagation stops at matching consumers, for time-
777
- // slicing. Continue the propagation here.
778
- if ( oldProps === null ) {
779
- valueDidChange = true ;
780
- propagateContextChange ( workInProgress , context , renderExpirationTime ) ;
781
- } else {
782
- const oldValue = oldProps !== null ? oldProps . __memoizedValue : null ;
783
- // Use Object.is to compare the new context value to the old value.
784
- if ( ! is ( newValue , oldValue ) ) {
785
- valueDidChange = true ;
786
- propagateContextChange (
787
- workInProgress ,
788
- context ,
789
- renderExpirationTime ,
790
- ) ;
791
- }
794
+ changedBits = providerFiber . stateNode ;
795
+ if ( changedBits !== 0 ) {
796
+ // Context change propagation stops at matching consumers, for time-
797
+ // slicing. Continue the propagation here.
798
+ propagateContextChange (
799
+ workInProgress ,
800
+ context ,
801
+ changedBits ,
802
+ renderExpirationTime ,
803
+ ) ;
792
804
}
793
805
}
794
806
795
- // The old context value is stored on the consumer object. We can't use the
796
- // provider's memoizedProps because those have already been updated by the
797
- // time we get here, in the provider's begin phase.
798
- newProps . __memoizedValue = newValue ;
807
+ // Store the bits on the fiber's stateNode for quick access.
808
+ let bits = newProps . bits ;
809
+ if ( bits === undefined || bits === null ) {
810
+ // Subscribe to all changes by default
811
+ bits = MAX_SIGNED_32_BIT_INT ;
812
+ }
813
+ workInProgress . stateNode = bits ;
799
814
800
815
if ( hasLegacyContextChanged ( ) ) {
801
816
// Normally we can bail out on props equality but if context has changed
802
817
// we don't do the bailout and we have to reuse existing props instead.
803
- } else if ( newProps === oldProps && ! valueDidChange ) {
818
+ } else if ( newProps === oldProps && changedBits === 0 ) {
804
819
return bailoutOnAlreadyFinishedWork ( current , workInProgress ) ;
805
820
}
806
821
const newChildren = newProps . render ( newValue ) ;
0 commit comments