88 */
99
1010import 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' ;
1612import type { Fiber } from 'react-reconciler/src/ReactFiber' ;
1713import type { HostContext } from './ReactFiberHostContext' ;
1814import type { HydrationContext } from './ReactFiberHydrationContext' ;
@@ -68,6 +64,7 @@ import {
6864} from './ReactFiberContext' ;
6965import { pushProvider } from './ReactFiberNewContext' ;
7066import { NoWork , Never } from './ReactFiberExpirationTime' ;
67+ import MAX_SIGNED_32_BIT_INT from './maxSigned32BitInt' ;
7168
7269let warnedAboutStatelessRefs ;
7370
@@ -616,6 +613,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
616613 function propagateContextChange < V > (
617614 workInProgress: Fiber,
618615 context: ReactContext< V > ,
616+ changedBits: number,
619617 renderExpirationTime: ExpirationTime,
620618 ): void {
621619 if ( enableNewContextAPI ) {
@@ -626,7 +624,8 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
626624 switch ( fiber . tag ) {
627625 case ConsumerComponent:
628626 // Check if the context matches.
629- if ( fiber . type . context === context ) {
627+ const bits = fiber . stateNode ;
628+ if ( fiber . type === context && ( bits & changedBits ) !== 0 ) {
630629 // Update the expiration time of all the ancestors, including
631630 // the alternates.
632631 let node = fiber ;
@@ -668,7 +667,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
668667 break ;
669668 case ProviderComponent :
670669 // 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 ;
672671 break ;
673672 default:
674673 // Traverse down.
@@ -724,12 +723,33 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
724723 workInProgress.memoizedProps = newProps;
725724
726725 const newValue = newProps.value;
727- const oldValue = oldProps !== null ? oldProps.value : null;
728726
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+ }
732751 }
752+ workInProgress . stateNode = changedBits ;
733753
734754 if ( oldProps !== null && oldProps . children === newProps . children ) {
735755 return bailoutOnAlreadyFinishedWork ( current , workInProgress ) ;
@@ -748,8 +768,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
748768 renderExpirationTime ,
749769 ) {
750770 if ( enableNewContextAPI ) {
751- const consumerType : ReactConsumerType < any > = workInProgress . type ;
752- const context : ReactContext < any > = consumerType . context ;
771+ const context : ReactContext < any > = workInProgress . type ;
753772
754773 const newProps = workInProgress . pendingProps ;
755774 const oldProps = workInProgress . memoizedProps ;
@@ -758,12 +777,12 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
758777 const providerFiber : Fiber | null = context . currentProvider ;
759778
760779 let newValue ;
761- let valueDidChange ;
780+ let changedBits ;
762781 if ( providerFiber === null ) {
763782 // This is a detached consumer (has no provider). Use the default
764783 // context value.
765784 newValue = context . defaultValue ;
766- valueDidChange = false ;
785+ changedBits = 0 ;
767786 } else {
768787 const provider = providerFiber . pendingProps ;
769788 invariant (
@@ -772,35 +791,31 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
772791 'a bug in React. Please file an issue.' ,
773792 ) ;
774793 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+ ) ;
792804 }
793805 }
794806
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 ;
799814
800815 if ( hasLegacyContextChanged ( ) ) {
801816 // Normally we can bail out on props equality but if context has changed
802817 // 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 ) {
804819 return bailoutOnAlreadyFinishedWork ( current , workInProgress ) ;
805820 }
806821 const newChildren = newProps . render ( newValue ) ;
0 commit comments