From b45ac1285f98a4db8ebdd338888ca30ae9f610f0 Mon Sep 17 00:00:00 2001 From: okmttdhr Date: Sat, 8 May 2021 14:31:06 +0900 Subject: [PATCH] Schedule work to update Context.Consumer inside Suspense --- .../src/ReactFiberNewContext.new.js | 70 +++++++++++++++++++ .../src/ReactFiberNewContext.old.js | 70 +++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/packages/react-reconciler/src/ReactFiberNewContext.new.js b/packages/react-reconciler/src/ReactFiberNewContext.new.js index c9933f2362ad3..7a3bf013bbbdf 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.new.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.new.js @@ -20,9 +20,12 @@ import type {SharedQueue} from './ReactUpdateQueue.new'; import {isPrimaryRenderer} from './ReactFiberHostConfig'; import {createCursor, push, pop} from './ReactFiberStack.new'; import { + ContextConsumer, ContextProvider, ClassComponent, DehydratedFragment, + OffscreenComponent, + SuspenseComponent, } from './ReactWorkTags'; import { NoLanes, @@ -157,6 +160,10 @@ export function scheduleWorkOnParentPath( !isSubsetOfLanes(alternate.childLanes, renderLanes) ) { alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes); + } else { + // Neither alternate was updated, which means the rest of the + // ancestor path already has sufficient priority. + break; } node = node.return; } @@ -361,6 +368,69 @@ function propagateContextChanges( } scheduleWorkOnParentPath(consumer.return, renderLanes); + // If Context.Consumer is a descendant of Suspense, + // `scheduleWorkOnParentPath` may break before it get to Suspense. + // Schedule work to avoid bailout before Context.Consumer receives the value. + const primaryChildFragment = workInProgress.child; + if ( + workInProgress.tag === SuspenseComponent && + primaryChildFragment !== null && + primaryChildFragment.tag === OffscreenComponent && + consumer.tag === ContextConsumer + ) { + const workInProgressAlternate = workInProgress.alternate; + if (!isSubsetOfLanes(workInProgress.childLanes, renderLanes)) { + workInProgress.childLanes = mergeLanes( + workInProgress.childLanes, + renderLanes, + ); + if (workInProgressAlternate !== null) { + workInProgressAlternate.childLanes = mergeLanes( + workInProgressAlternate.childLanes, + renderLanes, + ); + } + } else if ( + workInProgressAlternate !== null && + !isSubsetOfLanes( + workInProgressAlternate.childLanes, + renderLanes, + ) + ) { + workInProgressAlternate.childLanes = mergeLanes( + workInProgressAlternate.childLanes, + renderLanes, + ); + } + const primaryChildFragmentAlternate = + primaryChildFragment.alternate; + if ( + !isSubsetOfLanes(primaryChildFragment.childLanes, renderLanes) + ) { + primaryChildFragment.childLanes = mergeLanes( + primaryChildFragment.childLanes, + renderLanes, + ); + if (primaryChildFragmentAlternate !== null) { + primaryChildFragmentAlternate.childLanes = mergeLanes( + primaryChildFragmentAlternate.childLanes, + renderLanes, + ); + } + } else if ( + primaryChildFragmentAlternate !== null && + !isSubsetOfLanes( + primaryChildFragmentAlternate.childLanes, + renderLanes, + ) + ) { + primaryChildFragmentAlternate.childLanes = mergeLanes( + primaryChildFragmentAlternate.childLanes, + renderLanes, + ); + } + } + if (!forcePropagateEntireTree) { // During lazy propagation, when we find a match, we can defer // propagating changes to the children, because we're going to diff --git a/packages/react-reconciler/src/ReactFiberNewContext.old.js b/packages/react-reconciler/src/ReactFiberNewContext.old.js index daebdfd562798..6855891c82c4d 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.old.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.old.js @@ -20,9 +20,12 @@ import type {SharedQueue} from './ReactUpdateQueue.old'; import {isPrimaryRenderer} from './ReactFiberHostConfig'; import {createCursor, push, pop} from './ReactFiberStack.old'; import { + ContextConsumer, ContextProvider, ClassComponent, DehydratedFragment, + OffscreenComponent, + SuspenseComponent, } from './ReactWorkTags'; import { NoLanes, @@ -157,6 +160,10 @@ export function scheduleWorkOnParentPath( !isSubsetOfLanes(alternate.childLanes, renderLanes) ) { alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes); + } else { + // Neither alternate was updated, which means the rest of the + // ancestor path already has sufficient priority. + break; } node = node.return; } @@ -361,6 +368,69 @@ function propagateContextChanges( } scheduleWorkOnParentPath(consumer.return, renderLanes); + // If Context.Consumer is a descendant of Suspense, + // `scheduleWorkOnParentPath` may break before it get to Suspense. + // Schedule work to avoid bailout before Context.Consumer receives the value. + const primaryChildFragment = workInProgress.child; + if ( + workInProgress.tag === SuspenseComponent && + primaryChildFragment !== null && + primaryChildFragment.tag === OffscreenComponent && + consumer.tag === ContextConsumer + ) { + const workInProgressAlternate = workInProgress.alternate; + if (!isSubsetOfLanes(workInProgress.childLanes, renderLanes)) { + workInProgress.childLanes = mergeLanes( + workInProgress.childLanes, + renderLanes, + ); + if (workInProgressAlternate !== null) { + workInProgressAlternate.childLanes = mergeLanes( + workInProgressAlternate.childLanes, + renderLanes, + ); + } + } else if ( + workInProgressAlternate !== null && + !isSubsetOfLanes( + workInProgressAlternate.childLanes, + renderLanes, + ) + ) { + workInProgressAlternate.childLanes = mergeLanes( + workInProgressAlternate.childLanes, + renderLanes, + ); + } + const primaryChildFragmentAlternate = + primaryChildFragment.alternate; + if ( + !isSubsetOfLanes(primaryChildFragment.childLanes, renderLanes) + ) { + primaryChildFragment.childLanes = mergeLanes( + primaryChildFragment.childLanes, + renderLanes, + ); + if (primaryChildFragmentAlternate !== null) { + primaryChildFragmentAlternate.childLanes = mergeLanes( + primaryChildFragmentAlternate.childLanes, + renderLanes, + ); + } + } else if ( + primaryChildFragmentAlternate !== null && + !isSubsetOfLanes( + primaryChildFragmentAlternate.childLanes, + renderLanes, + ) + ) { + primaryChildFragmentAlternate.childLanes = mergeLanes( + primaryChildFragmentAlternate.childLanes, + renderLanes, + ); + } + } + if (!forcePropagateEntireTree) { // During lazy propagation, when we find a match, we can defer // propagating changes to the children, because we're going to