diff --git a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js index 9f954f4581d3c..b1a88868b3389 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js @@ -1905,10 +1905,19 @@ describe('ReactDOMServerPartialHydration', () => { resolve(); await promise; }); - expect(clicks).toBe(1); - - expect(container.textContent).toBe('Hello'); + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + expect(clicks).toBe(0); + expect(container.textContent).toBe('Click meHello'); + } else { + expect(clicks).toBe(1); + expect(container.textContent).toBe('Hello'); + } document.body.removeChild(container); }); @@ -1991,7 +2000,16 @@ describe('ReactDOMServerPartialHydration', () => { await promise; }); - expect(onEvent).toHaveBeenCalledTimes(2); + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + expect(onEvent).toHaveBeenCalledTimes(0); + } else { + expect(onEvent).toHaveBeenCalledTimes(2); + } document.body.removeChild(container); }); @@ -2072,7 +2090,17 @@ describe('ReactDOMServerPartialHydration', () => { resolve(); await promise; }); - expect(clicks).toBe(2); + + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + expect(clicks).toBe(0); + } else { + expect(clicks).toBe(2); + } document.body.removeChild(container); }); @@ -2158,7 +2186,16 @@ describe('ReactDOMServerPartialHydration', () => { resolve(); await promise; }); - expect(onEvent).toHaveBeenCalledTimes(2); + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + expect(onEvent).toHaveBeenCalledTimes(0); + } else { + expect(onEvent).toHaveBeenCalledTimes(2); + } document.body.removeChild(container); }); @@ -2231,9 +2268,19 @@ describe('ReactDOMServerPartialHydration', () => { await promise; }); - expect(clicksOnChild).toBe(1); - // This will be zero due to the stopPropagation. - expect(clicksOnParent).toBe(0); + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + expect(clicksOnChild).toBe(0); + expect(clicksOnParent).toBe(0); + } else { + expect(clicksOnChild).toBe(1); + // This will be zero due to the stopPropagation. + expect(clicksOnParent).toBe(0); + } document.body.removeChild(container); }); @@ -2310,8 +2357,16 @@ describe('ReactDOMServerPartialHydration', () => { }); // We're now full hydrated. - - expect(clicks).toBe(1); + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + expect(clicks).toBe(0); + } else { + expect(clicks).toBe(1); + } document.body.removeChild(parentContainer); }); @@ -2580,8 +2635,20 @@ describe('ReactDOMServerPartialHydration', () => { await promise; }); - expect(submits).toBe(1); - expect(container.textContent).toBe('Hello'); + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + // discrete event not replayed + expect(submits).toBe(0); + expect(container.textContent).toBe('Click meHello'); + } else { + expect(submits).toBe(1); + expect(container.textContent).toBe('Hello'); + } + document.body.removeChild(container); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js index 76d9b43d07f61..bb04b46df1c40 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js @@ -14,6 +14,7 @@ import {createEventTarget} from 'dom-event-testing-library'; let React; let ReactDOM; let ReactDOMServer; +let ReactFeatureFlags; let Scheduler; let Suspense; let act; @@ -112,7 +113,7 @@ describe('ReactDOMServerSelectiveHydration', () => { beforeEach(() => { jest.resetModuleRegistry(); - const ReactFeatureFlags = require('shared/ReactFeatureFlags'); + ReactFeatureFlags = require('shared/ReactFeatureFlags'); ReactFeatureFlags.enableCreateEventHandleAPI = true; React = require('react'); ReactDOM = require('react-dom'); @@ -266,9 +267,19 @@ describe('ReactDOMServerSelectiveHydration', () => { resolve(); await promise; }); - // After the click, we should prioritize D and the Click first, - // and only after that render A and C. - expect(Scheduler).toHaveYielded(['D', 'Clicked D', 'A']); + + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + expect(Scheduler).toHaveYielded(['D', 'A']); + } else { + // After the click, we should prioritize D and the Click first, + // and only after that render A and C. + expect(Scheduler).toHaveYielded(['D', 'Clicked D', 'A']); + } document.body.removeChild(container); }); @@ -343,7 +354,16 @@ describe('ReactDOMServerSelectiveHydration', () => { dispatchClickEvent(spanC); dispatchClickEvent(spanD); - expect(Scheduler).toHaveYielded(['App']); + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + expect(Scheduler).toHaveYielded(['App', 'C', 'Clicked C']); + } else { + expect(Scheduler).toHaveYielded(['App']); + } await act(async () => { suspend = false; @@ -351,18 +371,29 @@ describe('ReactDOMServerSelectiveHydration', () => { await promise; }); - // We should prioritize hydrating A, C and D first since we clicked in - // them. Only after they're done will we hydrate B. - expect(Scheduler).toHaveYielded([ - 'A', - 'Clicked A', - 'C', - 'Clicked C', - 'D', - 'Clicked D', - // B should render last since it wasn't clicked. - 'B', - ]); + if ( + ReactFeatureFlags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay + ) { + expect(Scheduler).toHaveYielded([ + 'A', + 'D', + // B should render last since it wasn't clicked. + 'B', + ]); + } else { + // We should prioritize hydrating A, C and D first since we clicked in + // them. Only after they're done will we hydrate B. + expect(Scheduler).toHaveYielded([ + 'A', + 'Clicked A', + 'C', + 'Clicked C', + 'D', + 'Clicked D', + // B should render last since it wasn't clicked. + 'B', + ]); + } document.body.removeChild(container); }); @@ -519,7 +550,17 @@ describe('ReactDOMServerSelectiveHydration', () => { resolve(); await promise; }); - expect(Scheduler).toHaveYielded(['D', 'Clicked D', 'A']); + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + // no replay + expect(Scheduler).toHaveYielded(['D', 'A']); + } else { + expect(Scheduler).toHaveYielded(['D', 'Clicked D', 'A']); + } document.body.removeChild(container); }); @@ -599,26 +640,42 @@ describe('ReactDOMServerSelectiveHydration', () => { createEventTarget(spanC).virtualclick(); createEventTarget(spanD).virtualclick(); - expect(Scheduler).toHaveYielded(['App']); - + if ( + ReactFeatureFlags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay + ) { + expect(Scheduler).toHaveYielded(['App', 'C', 'Clicked C']); + } else { + expect(Scheduler).toHaveYielded(['App']); + } await act(async () => { suspend = false; resolve(); await promise; }); - // We should prioritize hydrating A, C and D first since we clicked in - // them. Only after they're done will we hydrate B. - expect(Scheduler).toHaveYielded([ - 'A', - 'Clicked A', - 'C', - 'Clicked C', - 'D', - 'Clicked D', - // B should render last since it wasn't clicked. - 'B', - ]); + if ( + ReactFeatureFlags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay + ) { + expect(Scheduler).toHaveYielded([ + 'A', + 'D', + // B should render last since it wasn't clicked. + 'B', + ]); + } else { + // We should prioritize hydrating A, C and D first since we clicked in + // them. Only after they're done will we hydrate B. + expect(Scheduler).toHaveYielded([ + 'A', + 'Clicked A', + 'C', + 'Clicked C', + 'D', + 'Clicked D', + // B should render last since it wasn't clicked. + 'B', + ]); + } document.body.removeChild(container); }); @@ -707,20 +764,37 @@ describe('ReactDOMServerSelectiveHydration', () => { await promise; }); - // We should prioritize hydrating D first because we clicked it. - // Next we should hydrate C since that's the current hover target. - // To simplify implementation details we hydrate both B and C at - // the same time since B was already scheduled. - // This is ok because it will at least not continue for nested - // boundary. See the next test below. - expect(Scheduler).toHaveYielded([ - 'D', - 'Clicked D', - 'B', // Ideally this should be later. - 'C', - 'Hover C', - 'A', - ]); + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + // We should prioritize hydrating D first because we clicked it. + // but event isnt replayed + expect(Scheduler).toHaveYielded([ + 'D', + 'B', // Ideally this should be later. + 'C', + 'Hover C', + 'A', + ]); + } else { + // We should prioritize hydrating D first because we clicked it. + // Next we should hydrate C since that's the current hover target. + // To simplify implementation details we hydrate both B and C at + // the same time since B was already scheduled. + // This is ok because it will at least not continue for nested + // boundary. See the next test below. + expect(Scheduler).toHaveYielded([ + 'D', + 'Clicked D', + 'B', // Ideally this should be later. + 'C', + 'Hover C', + 'A', + ]); + } document.body.removeChild(container); }); @@ -796,16 +870,18 @@ describe('ReactDOMServerSelectiveHydration', () => { dispatchMouseHoverEvent(spanB, spanD); dispatchMouseHoverEvent(spanC, spanB); - suspend = false; - resolve(); - await promise; + await act(async () => { + suspend = false; + resolve(); + await promise; + }); // We should prioritize hydrating D first because we clicked it. // Next we should hydrate C since that's the current hover target. // Next it doesn't matter if we hydrate A or B first but as an // implementation detail we're currently hydrating B first since // we at one point hovered over it and we never deprioritized it. - expect(Scheduler).toFlushAndYield(['App', 'C', 'Hover C', 'A', 'B', 'D']); + expect(Scheduler).toHaveYielded(['App', 'C', 'Hover C', 'A', 'B', 'D']); document.body.removeChild(container); }); @@ -970,4 +1046,88 @@ describe('ReactDOMServerSelectiveHydration', () => { document.body.removeChild(container); }); + + // @gate enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay + it('fires capture event handlers and native events if content is hydratable during discrete event', async () => { + spyOnDev(console, 'error'); + function Child({text}) { + Scheduler.unstable_yieldValue(text); + const ref = React.useRef(); + React.useLayoutEffect(() => { + if (!ref.current) { + return; + } + ref.current.onclick = () => { + Scheduler.unstable_yieldValue('Native Click ' + text); + }; + }, [text]); + return ( + { + Scheduler.unstable_yieldValue('Capture Clicked ' + text); + }} + onClick={e => { + Scheduler.unstable_yieldValue('Clicked ' + text); + }}> + {text} + + ); + } + + function App() { + Scheduler.unstable_yieldValue('App'); + return ( +
+ + + + + + +
+ ); + } + + let finalHTML; + expect(() => { + finalHTML = ReactDOMServer.renderToString(); + }).toErrorDev([ + 'useLayoutEffect does nothing on the server', + 'useLayoutEffect does nothing on the server', + ]); + + expect(Scheduler).toHaveYielded(['App', 'A', 'B']); + + const container = document.createElement('div'); + // We need this to be in the document since we'll dispatch events on it. + document.body.appendChild(container); + + container.innerHTML = finalHTML; + + const span = container.getElementsByTagName('span')[1]; + + ReactDOM.hydrateRoot(container, ); + + // Nothing has been hydrated so far. + expect(Scheduler).toHaveYielded([]); + + // This should synchronously hydrate the root App and the second suspense + // boundary. + dispatchClickEvent(span); + + // We rendered App, B and then invoked the event without rendering A. + expect(Scheduler).toHaveYielded([ + 'App', + 'B', + 'Capture Clicked B', + 'Native Click B', + 'Clicked B', + ]); + + // After continuing the scheduler, we finally hydrate A. + expect(Scheduler).toFlushAndYield(['A']); + + document.body.removeChild(container); + }); }); diff --git a/packages/react-dom/src/events/ReactDOMEventListener.js b/packages/react-dom/src/events/ReactDOMEventListener.js index dfe3c2e3c00c0..6467421d4f635 100644 --- a/packages/react-dom/src/events/ReactDOMEventListener.js +++ b/packages/react-dom/src/events/ReactDOMEventListener.js @@ -11,13 +11,18 @@ import type {AnyNativeEvent} from '../events/PluginModuleType'; import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes'; import type {Container, SuspenseInstance} from '../client/ReactDOMHostConfig'; import type {DOMEventName} from '../events/DOMEventNames'; - +import { + enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + enableSelectiveHydration, +} from 'shared/ReactFeatureFlags'; import { isReplayableDiscreteEvent, queueDiscreteEvent, hasQueuedDiscreteEvents, clearIfContinuousEvent, queueIfContinuousEvent, + attemptSynchronousHydration, + isCapturePhaseSynchronouslyHydratableEvent, } from './ReactDOMEventReplaying'; import { getNearestMountedFiber, @@ -28,7 +33,10 @@ import {HostRoot, SuspenseComponent} from 'react-reconciler/src/ReactWorkTags'; import {type EventSystemFlags, IS_CAPTURE_PHASE} from './EventSystemFlags'; import getEventTarget from './getEventTarget'; -import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree'; +import { + getInstanceFromNode, + getClosestInstanceFromNode, +} from '../client/ReactDOMComponentTree'; import {dispatchEventForPluginEventSystem} from './DOMPluginEventSystem'; @@ -176,7 +184,7 @@ export function dispatchEvent( return; } - const blockedOn = attemptToDispatchEvent( + let blockedOn = attemptToDispatchEvent( domEventName, eventSystemFlags, targetContainer, @@ -192,7 +200,10 @@ export function dispatchEvent( } if (allowReplay) { - if (isReplayableDiscreteEvent(domEventName)) { + if ( + !enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay && + isReplayableDiscreteEvent(domEventName) + ) { // This this to be replayed later once the target is available. queueDiscreteEvent( blockedOn, @@ -219,6 +230,29 @@ export function dispatchEvent( clearIfContinuousEvent(domEventName, nativeEvent); } + if ( + enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay && + enableSelectiveHydration && + isCapturePhaseSynchronouslyHydratableEvent(domEventName) + ) { + while (blockedOn !== null) { + const fiber = getInstanceFromNode(blockedOn); + if (fiber !== null) { + attemptSynchronousHydration(fiber); + } + const nextBlockedOn = attemptToDispatchEvent( + domEventName, + eventSystemFlags, + targetContainer, + nativeEvent, + ); + if (nextBlockedOn === blockedOn) { + break; + } + blockedOn = nextBlockedOn; + } + } + // This is not replayable so we'll invoke it but without a target, // in case the event system needs to trace it. dispatchEventForPluginEventSystem( diff --git a/packages/react-dom/src/events/ReactDOMEventReplaying.js b/packages/react-dom/src/events/ReactDOMEventReplaying.js index eab9b4650c108..c363721bcd848 100644 --- a/packages/react-dom/src/events/ReactDOMEventReplaying.js +++ b/packages/react-dom/src/events/ReactDOMEventReplaying.js @@ -14,7 +14,10 @@ import type {EventSystemFlags} from './EventSystemFlags'; import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes'; import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities'; -import {enableSelectiveHydration} from 'shared/ReactFeatureFlags'; +import { + enableSelectiveHydration, + enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, +} from 'shared/ReactFeatureFlags'; import { unstable_scheduleCallback as scheduleCallback, unstable_NormalPriority as NormalPriority, @@ -32,10 +35,14 @@ import { import {HostRoot, SuspenseComponent} from 'react-reconciler/src/ReactWorkTags'; import {isHigherEventPriority} from 'react-reconciler/src/ReactEventPriorities'; -let attemptSynchronousHydration: (fiber: Object) => void; +let _attemptSynchronousHydration: (fiber: Object) => void; export function setAttemptSynchronousHydration(fn: (fiber: Object) => void) { - attemptSynchronousHydration = fn; + _attemptSynchronousHydration = fn; +} + +export function attemptSynchronousHydration(fiber: Object) { + _attemptSynchronousHydration(fiber); } let attemptDiscreteHydration: (fiber: Object) => void; @@ -180,6 +187,9 @@ export function queueDiscreteEvent( targetContainer: EventTarget, nativeEvent: AnyNativeEvent, ): void { + if (enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) { + return; + } const queuedEvent = createQueuedReplayableEvent( blockedOn, domEventName, @@ -290,6 +300,13 @@ function accumulateOrCreateContinuousQueuedReplayableEvent( return existingQueuedEvent; } +export function isCapturePhaseSynchronouslyHydratableEvent( + eventName: DOMEventName, +) { + // TODO: maybe include more events + return isReplayableDiscreteEvent(eventName); +} + export function queueIfContinuousEvent( blockedOn: null | Container | SuspenseInstance, domEventName: DOMEventName, @@ -483,39 +500,41 @@ function attemptReplayContinuousQueuedEventInMap( function replayUnblockedEvents() { hasScheduledReplayAttempt = false; - // First replay discrete events. - while (queuedDiscreteEvents.length > 0) { - const nextDiscreteEvent = queuedDiscreteEvents[0]; - if (nextDiscreteEvent.blockedOn !== null) { - // We're still blocked. - // Increase the priority of this boundary to unblock - // the next discrete event. - const fiber = getInstanceFromNode(nextDiscreteEvent.blockedOn); - if (fiber !== null) { - attemptDiscreteHydration(fiber); - } - break; - } - const targetContainers = nextDiscreteEvent.targetContainers; - while (targetContainers.length > 0) { - const targetContainer = targetContainers[0]; - const nextBlockedOn = attemptToDispatchEvent( - nextDiscreteEvent.domEventName, - nextDiscreteEvent.eventSystemFlags, - targetContainer, - nextDiscreteEvent.nativeEvent, - ); - if (nextBlockedOn !== null) { - // We're still blocked. Try again later. - nextDiscreteEvent.blockedOn = nextBlockedOn; + if (!enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) { + // First replay discrete events. + while (queuedDiscreteEvents.length > 0) { + const nextDiscreteEvent = queuedDiscreteEvents[0]; + if (nextDiscreteEvent.blockedOn !== null) { + // We're still blocked. + // Increase the priority of this boundary to unblock + // the next discrete event. + const fiber = getInstanceFromNode(nextDiscreteEvent.blockedOn); + if (fiber !== null) { + attemptDiscreteHydration(fiber); + } break; } - // This target container was successfully dispatched. Try the next. - targetContainers.shift(); - } - if (nextDiscreteEvent.blockedOn === null) { - // We've successfully replayed the first event. Let's try the next one. - queuedDiscreteEvents.shift(); + const targetContainers = nextDiscreteEvent.targetContainers; + while (targetContainers.length > 0) { + const targetContainer = targetContainers[0]; + const nextBlockedOn = attemptToDispatchEvent( + nextDiscreteEvent.domEventName, + nextDiscreteEvent.eventSystemFlags, + targetContainer, + nextDiscreteEvent.nativeEvent, + ); + if (nextBlockedOn !== null) { + // We're still blocked. Try again later. + nextDiscreteEvent.blockedOn = nextBlockedOn; + break; + } + // This target container was successfully dispatched. Try the next. + targetContainers.shift(); + } + if (nextDiscreteEvent.blockedOn === null) { + // We've successfully replayed the first event. Let's try the next one. + queuedDiscreteEvents.shift(); + } } } // Next replay any continuous events. diff --git a/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js b/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js index 4f2df6fffc495..1b2bfbb0bef4d 100644 --- a/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js +++ b/packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js @@ -649,7 +649,16 @@ describe('DOMPluginEventSystem', () => { // We're now full hydrated. - expect(clicks).toBe(1); + if ( + gate( + flags => + flags.enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, + ) + ) { + expect(clicks).toBe(0); + } else { + expect(clicks).toBe(1); + } document.body.removeChild(parentContainer); }); diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 608db694be518..f5d34e2ff6539 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -103,6 +103,8 @@ export const warnOnSubscriptionInsideStartTransition = false; export const enableSuspenseAvoidThisFallback = false; +export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = false; + export const enableComponentStackLocations = true; export const enableNewReconciler = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 9c3d45528c5af..5a1c1ed8d9c60 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -50,6 +50,7 @@ export const warnUnstableRenderSubtreeIntoContainer = false; export const warnAboutSpreadingKeyToJSX = false; export const warnOnSubscriptionInsideStartTransition = false; export const enableSuspenseAvoidThisFallback = false; +export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = false; export const enableComponentStackLocations = false; export const enableLegacyFBSupport = false; export const enableFilterEmptyStringAttributesDOM = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index bf96cc00e687c..3c11070d6ecb3 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -41,6 +41,7 @@ export const warnUnstableRenderSubtreeIntoContainer = false; export const warnAboutSpreadingKeyToJSX = false; export const warnOnSubscriptionInsideStartTransition = false; export const enableSuspenseAvoidThisFallback = false; +export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = false; export const enableComponentStackLocations = false; export const enableLegacyFBSupport = false; export const enableFilterEmptyStringAttributesDOM = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 0d2b226adf2f6..4b0457c219587 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -41,6 +41,7 @@ export const warnUnstableRenderSubtreeIntoContainer = false; export const warnAboutSpreadingKeyToJSX = false; export const warnOnSubscriptionInsideStartTransition = false; export const enableSuspenseAvoidThisFallback = false; +export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = false; export const enableComponentStackLocations = true; export const enableLegacyFBSupport = false; export const enableFilterEmptyStringAttributesDOM = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index f760425ce03cb..2c54c1fb77c20 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -51,6 +51,7 @@ export const enableNewReconciler = false; export const deferRenderPhaseUpdateToNextBatch = false; export const warnOnSubscriptionInsideStartTransition = false; export const enableSuspenseAvoidThisFallback = false; +export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = false; export const enableStrictEffects = false; export const createRootStrictEffectsByDefault = false; export const enableUseRefAccessWarning = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index cb0efcc34bdcd..be60bcfbddb77 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -41,6 +41,7 @@ export const warnUnstableRenderSubtreeIntoContainer = false; export const warnAboutSpreadingKeyToJSX = false; export const warnOnSubscriptionInsideStartTransition = false; export const enableSuspenseAvoidThisFallback = false; +export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = false; export const enableComponentStackLocations = true; export const enableLegacyFBSupport = false; export const enableFilterEmptyStringAttributesDOM = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index d1cfdbb749565..76af047ab6d4e 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -41,6 +41,7 @@ export const warnUnstableRenderSubtreeIntoContainer = false; export const warnAboutSpreadingKeyToJSX = false; export const warnOnSubscriptionInsideStartTransition = false; export const enableSuspenseAvoidThisFallback = false; +export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = false; export const enableComponentStackLocations = true; export const enableLegacyFBSupport = false; export const enableFilterEmptyStringAttributesDOM = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index 62a10f475acaa..6fc6cf3c68367 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -41,6 +41,7 @@ export const warnUnstableRenderSubtreeIntoContainer = false; export const warnAboutSpreadingKeyToJSX = false; export const warnOnSubscriptionInsideStartTransition = false; export const enableSuspenseAvoidThisFallback = false; +export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = false; export const enableComponentStackLocations = true; export const enableLegacyFBSupport = !__EXPERIMENTAL__; export const enableFilterEmptyStringAttributesDOM = false; diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index 6400ef0422503..ef980529f3f21 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -27,6 +27,7 @@ export const enableLazyContextPropagation = __VARIANT__; export const enableSyncDefaultUpdates = __VARIANT__; export const consoleManagedByDevToolsDuringStrictMode = __VARIANT__; export const warnOnSubscriptionInsideStartTransition = __VARIANT__; +export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = __VARIANT__; // Enable this flag to help with concurrent mode debugging. // It logs information to the console about React scheduling, rendering, and commit phases. diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index c5dde53578c95..0f1ce6eb1f9f7 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -32,6 +32,7 @@ export const { enableLazyContextPropagation, enableSyncDefaultUpdates, warnOnSubscriptionInsideStartTransition, + enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay, } = dynamicFeatureFlags; // On WWW, __EXPERIMENTAL__ is used for a new modern build.