From 9b052b68dc9a31907f59aff2fad7e46fadae27fc Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 11 Nov 2020 09:44:56 -0500 Subject: [PATCH] Add Profiler callback when nested udpates are scheduled This callback accepts the no parameters (except for the current interactions). Users of this hook can inspect the call stack to access and log the source location of the component. --- .../src/ReactFiberWorkLoop.new.js | 38 ++ .../src/ReactFiberWorkLoop.old.js | 40 ++ .../__tests__/ReactProfiler-test.internal.js | 524 ++++++++++++++++++ packages/shared/ReactFeatureFlags.js | 4 + .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + .../ReactFeatureFlags.test-renderer.native.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../shared/forks/ReactFeatureFlags.testing.js | 1 + .../forks/ReactFeatureFlags.testing.www.js | 1 + .../forks/ReactFeatureFlags.www-dynamic.js | 2 + .../shared/forks/ReactFeatureFlags.www.js | 2 + 13 files changed, 617 insertions(+) diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 9153c03049b..4ccd2ea67e8 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -22,6 +22,7 @@ import { replayFailedUnitOfWorkWithInvokeGuardedCallback, enableProfilerTimer, enableProfilerNestedUpdatePhase, + enableProfilerNestedUpdateScheduledHook, enableSchedulerTracing, warnAboutUnmockedScheduler, deferRenderPhaseUpdateToNextBatch, @@ -110,6 +111,7 @@ import { ForwardRef, MemoComponent, SimpleMemoComponent, + Profiler, } from './ReactWorkTags'; import {LegacyRoot} from './ReactRootTags'; import { @@ -258,6 +260,10 @@ let workInProgress: Fiber | null = null; // The lanes we're rendering let workInProgressRootRenderLanes: Lanes = NoLanes; +// Only used when enableProfilerNestedUpdateScheduledHook is true; +// to track which root is currently committing layout effects. +let rootCommittingMutationOrLayoutEffects: FiberRoot | null = null; + // Stack that allows components to change the render lanes for its subtree // This is a superset of the lanes we started working on at the root. The only // case where it's different from `workInProgressRootRenderLanes` is when we @@ -509,6 +515,30 @@ export function scheduleUpdateOnFiber( // Mark that the root has a pending update. markRootUpdated(root, lane, eventTime); + if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) { + if ( + executionContext === CommitContext && + root === rootCommittingMutationOrLayoutEffects + ) { + if (fiber.mode & ProfileMode) { + let current = fiber; + while (current !== null) { + if (current.tag === Profiler) { + const {onNestedUpdateScheduled} = current.memoizedProps; + if (typeof onNestedUpdateScheduled === 'function') { + if (enableSchedulerTracing) { + onNestedUpdateScheduled(root.memoizedInteractions); + } else { + onNestedUpdateScheduled(); + } + } + } + current = current.return; + } + } + } + } + if (root === workInProgressRoot) { // Received an update to a tree that's in the middle of rendering. Mark // that there was an interleaved update work on this root. Unless the @@ -1898,6 +1928,10 @@ function commitRootImpl(root, renderPriorityLevel) { recordCommitTime(); } + if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) { + rootCommittingMutationOrLayoutEffects = root; + } + // The next phase is the mutation phase, where we mutate the host tree. commitMutationEffects(finishedWork, root, renderPriorityLevel); @@ -1936,6 +1970,10 @@ function commitRootImpl(root, renderPriorityLevel) { markLayoutEffectsStopped(); } + if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) { + rootCommittingMutationOrLayoutEffects = null; + } + // Tell Scheduler to yield at the end of the frame, so the browser has an // opportunity to paint. requestPaint(); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index b2476dad5b6..609bd8016b9 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -23,6 +23,7 @@ import { enableProfilerTimer, enableProfilerCommitHooks, enableProfilerNestedUpdatePhase, + enableProfilerNestedUpdateScheduledHook, enableSchedulerTracing, warnAboutUnmockedScheduler, deferRenderPhaseUpdateToNextBatch, @@ -112,6 +113,7 @@ import { OffscreenComponent, LegacyHiddenComponent, ScopeComponent, + Profiler, } from './ReactWorkTags'; import {LegacyRoot} from './ReactRootTags'; import { @@ -329,6 +331,10 @@ let hasUncaughtError = false; let firstUncaughtError = null; let legacyErrorBoundariesThatAlreadyFailed: Set | null = null; +// Only used when enableProfilerNestedUpdateScheduledHook is true; +// to track which root is currently committing layout effects. +let rootCommittingMutationOrLayoutEffects: FiberRoot | null = null; + let rootDoesHavePassiveEffects: boolean = false; let rootWithPendingPassiveEffects: FiberRoot | null = null; let pendingPassiveEffectsRenderPriority: ReactPriorityLevel = NoSchedulerPriority; @@ -533,6 +539,30 @@ export function scheduleUpdateOnFiber( // Mark that the root has a pending update. markRootUpdated(root, lane, eventTime); + if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) { + if ( + executionContext === CommitContext && + root === rootCommittingMutationOrLayoutEffects + ) { + if (fiber.mode & ProfileMode) { + let current = fiber; + while (current !== null) { + if (current.tag === Profiler) { + const {onNestedUpdateScheduled} = current.memoizedProps; + if (typeof onNestedUpdateScheduled === 'function') { + if (enableSchedulerTracing) { + onNestedUpdateScheduled(root.memoizedInteractions); + } else { + onNestedUpdateScheduled(); + } + } + } + current = current.return; + } + } + } + } + if (root === workInProgressRoot) { // Received an update to a tree that's in the middle of rendering. Mark // that there was an interleaved update work on this root. Unless the @@ -2047,6 +2077,12 @@ function commitRootImpl(root, renderPriorityLevel) { recordCommitTime(); } + if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) { + // Track the root here, rather than in commitLayoutEffects(), because of ref setters. + // Updates scheduled during ref detachment should also be flagged. + rootCommittingMutationOrLayoutEffects = root; + } + // The next phase is the mutation phase, where we mutate the host tree. nextEffect = firstEffect; do { @@ -2112,6 +2148,10 @@ function commitRootImpl(root, renderPriorityLevel) { nextEffect = null; + if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) { + rootCommittingMutationOrLayoutEffects = null; + } + // Tell Scheduler to yield at the end of the frame, so the browser has an // opportunity to paint. requestPaint(); diff --git a/packages/react/src/__tests__/ReactProfiler-test.internal.js b/packages/react/src/__tests__/ReactProfiler-test.internal.js index d9c70ae73b7..92b30d9280f 100644 --- a/packages/react/src/__tests__/ReactProfiler-test.internal.js +++ b/packages/react/src/__tests__/ReactProfiler-test.internal.js @@ -29,6 +29,7 @@ function loadModules({ enableProfilerTimer = true, enableProfilerCommitHooks = true, enableProfilerNestedUpdatePhase = true, + enableProfilerNestedUpdateScheduledHook = false, enableSchedulerTracing = true, replayFailedUnitOfWorkWithInvokeGuardedCallback = false, useNoopRenderer = false, @@ -38,6 +39,7 @@ function loadModules({ ReactFeatureFlags.enableProfilerTimer = enableProfilerTimer; ReactFeatureFlags.enableProfilerCommitHooks = enableProfilerCommitHooks; ReactFeatureFlags.enableProfilerNestedUpdatePhase = enableProfilerNestedUpdatePhase; + ReactFeatureFlags.enableProfilerNestedUpdateScheduledHook = enableProfilerNestedUpdateScheduledHook; ReactFeatureFlags.enableSchedulerTracing = enableSchedulerTracing; ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = replayFailedUnitOfWorkWithInvokeGuardedCallback; @@ -2438,6 +2440,528 @@ describe('Profiler', () => { }); } }); + + describe(`onNestedUpdateScheduled enableSchedulerTracing:${ + enableSchedulerTracing ? 'enabled' : 'disabled' + }`, () => { + beforeEach(() => { + jest.resetModules(); + + loadModules({ + enableProfilerNestedUpdateScheduledHook: true, + enableSchedulerTracing, + useNoopRenderer: true, + }); + }); + + it('is not called when the legacy render API is used to schedule an update', () => { + const onNestedUpdateScheduled = jest.fn(); + + ReactNoop.renderLegacySyncRoot( + +
initial
+
, + ); + + ReactNoop.renderLegacySyncRoot( + +
update
+
, + ); + + expect(onNestedUpdateScheduled).not.toHaveBeenCalled(); + }); + + it('is not called when the root API is used to schedule an update', () => { + const onNestedUpdateScheduled = jest.fn(); + + ReactNoop.render( + +
initial
+
, + ); + + ReactNoop.render( + +
update
+
, + ); + + expect(onNestedUpdateScheduled).not.toHaveBeenCalled(); + }); + + it('is called when a function component schedules an update during a layout effect', () => { + function Component() { + const [didMount, setDidMount] = React.useState(false); + React.useLayoutEffect(() => { + setDidMount(true); + }, []); + Scheduler.unstable_yieldValue(`Component:${didMount}`); + return didMount; + } + + const interactionCreation = { + id: 0, + name: 'creation event', + timestamp: Scheduler.unstable_now(), + }; + + const onNestedUpdateScheduled = jest.fn(); + + SchedulerTracing.unstable_trace( + interactionCreation.name, + Scheduler.unstable_now(), + () => { + ReactNoop.act(() => { + ReactNoop.render( + + + , + ); + }); + }, + ); + + expect(Scheduler).toHaveYielded(['Component:false', 'Component:true']); + expect(onNestedUpdateScheduled).toHaveBeenCalledTimes(1); + if (ReactFeatureFlags.enableSchedulerTracing) { + expect(onNestedUpdateScheduled.mock.calls[0][0]).toMatchInteractions([ + interactionCreation, + ]); + } + }); + + it('bubbles up and calls all ancestor Profilers', () => { + function Component() { + const [didMount, setDidMount] = React.useState(false); + React.useLayoutEffect(() => { + setDidMount(true); + }, []); + Scheduler.unstable_yieldValue(`Component:${didMount}`); + return didMount; + } + const onNestedUpdateScheduledOne = jest.fn(); + const onNestedUpdateScheduledTwo = jest.fn(); + const onNestedUpdateScheduledThree = jest.fn(); + + ReactNoop.act(() => { + ReactNoop.render( + + + <> + + + + + , + ); + }); + + expect(Scheduler).toHaveYielded(['Component:false', 'Component:true']); + expect(onNestedUpdateScheduledOne).toHaveBeenCalledTimes(1); + expect(onNestedUpdateScheduledTwo).toHaveBeenCalledTimes(1); + expect(onNestedUpdateScheduledThree).not.toHaveBeenCalled(); + }); + + it('is not called when an update is scheduled for another doort during a layout effect', () => { + const setStateRef = React.createRef(null); + + function ComponentRootOne() { + const [state, setState] = React.useState(false); + setStateRef.current = setState; + Scheduler.unstable_yieldValue(`ComponentRootOne:${state}`); + return state; + } + + function ComponentRootTwo() { + React.useLayoutEffect(() => { + setStateRef.current(true); + }, []); + Scheduler.unstable_yieldValue('ComponentRootTwo'); + return null; + } + + const interactionCreation = { + id: 0, + name: 'creation event', + timestamp: Scheduler.unstable_now(), + }; + + const onNestedUpdateScheduled = jest.fn(); + + SchedulerTracing.unstable_trace( + interactionCreation.name, + Scheduler.unstable_now(), + () => { + ReactNoop.act(() => { + ReactNoop.renderToRootWithID( + + + , + 1, + ); + + ReactNoop.renderToRootWithID( + + + , + 2, + ); + }); + }, + ); + + expect(Scheduler).toHaveYielded([ + 'ComponentRootOne:false', + 'ComponentRootTwo', + 'ComponentRootOne:true', + ]); + expect(onNestedUpdateScheduled).not.toHaveBeenCalled(); + }); + + it('is not called when a function component schedules an update during a passive effect', () => { + function Component() { + const [didMount, setDidMount] = React.useState(false); + React.useEffect(() => { + setDidMount(true); + }, []); + Scheduler.unstable_yieldValue(`Component:${didMount}`); + return didMount; + } + + const onNestedUpdateScheduled = jest.fn(); + + ReactNoop.act(() => { + ReactNoop.render( + + + , + ); + }); + + expect(Scheduler).toHaveYielded(['Component:false', 'Component:true']); + expect(onNestedUpdateScheduled).not.toHaveBeenCalled(); + }); + + it('is not called when a function component schedules an update outside of render', () => { + const updateFnRef = React.createRef(null); + + function Component() { + const [state, setState] = React.useState(false); + updateFnRef.current = () => setState(true); + Scheduler.unstable_yieldValue(`Component:${state}`); + return state; + } + + const onNestedUpdateScheduled = jest.fn(); + + ReactNoop.act(() => { + ReactNoop.render( + + + , + ); + }); + expect(Scheduler).toHaveYielded(['Component:false']); + + ReactNoop.act(() => { + updateFnRef.current(); + }); + expect(Scheduler).toHaveYielded(['Component:true']); + expect(onNestedUpdateScheduled).not.toHaveBeenCalled(); + }); + + it('it is not called when a component schedules an update during render', () => { + function Component() { + const [state, setState] = React.useState(false); + if (state === false) { + setState(true); + } + Scheduler.unstable_yieldValue(`Component:${state}`); + return state; + } + + const onNestedUpdateScheduled = jest.fn(); + + ReactNoop.act(() => { + ReactNoop.render( + + + , + ); + }); + + expect(Scheduler).toHaveYielded(['Component:false', 'Component:true']); + expect(onNestedUpdateScheduled).not.toHaveBeenCalled(); + }); + + it('it is called when a component schedules an update from a ref callback', () => { + function Component({mountChild}) { + const [refAttached, setRefAttached] = React.useState(false); + const [refDetached, setRefDetached] = React.useState(false); + const refSetter = React.useCallback(ref => { + if (ref !== null) { + setRefAttached(true); + } else { + setRefDetached(true); + } + }, []); + Scheduler.unstable_yieldValue( + `Component:${refAttached}:${refDetached}`, + ); + return mountChild ?
: null; + } + + const onNestedUpdateScheduled = jest.fn(); + + const interactionCreation = { + id: 0, + name: 'creation event', + timestamp: Scheduler.unstable_now(), + }; + + SchedulerTracing.unstable_trace( + interactionCreation.name, + Scheduler.unstable_now(), + () => { + ReactNoop.act(() => { + ReactNoop.render( + + + , + ); + }); + }, + ); + + expect(Scheduler).toHaveYielded([ + 'Component:false:false', + 'Component:true:false', + ]); + expect(onNestedUpdateScheduled).toHaveBeenCalledTimes(1); + if (ReactFeatureFlags.enableSchedulerTracing) { + expect(onNestedUpdateScheduled.mock.calls[0][0]).toMatchInteractions([ + interactionCreation, + ]); + } + + const interactionUpdate = { + id: 1, + name: 'update event', + timestamp: Scheduler.unstable_now(), + }; + + SchedulerTracing.unstable_trace( + interactionUpdate.name, + Scheduler.unstable_now(), + () => { + ReactNoop.act(() => { + ReactNoop.render( + + + , + ); + }); + }, + ); + + expect(Scheduler).toHaveYielded([ + 'Component:true:false', + 'Component:true:true', + ]); + expect(onNestedUpdateScheduled).toHaveBeenCalledTimes(2); + if (ReactFeatureFlags.enableSchedulerTracing) { + expect(onNestedUpdateScheduled.mock.calls[1][0]).toMatchInteractions([ + interactionUpdate, + ]); + } + }); + + it('is called when a class component schedules an update from the componentDidMount lifecycles', () => { + class Component extends React.Component { + state = { + value: false, + }; + componentDidMount() { + this.setState({value: true}); + } + render() { + const {value} = this.state; + Scheduler.unstable_yieldValue(`Component:${value}`); + return value; + } + } + + const interactionCreation = { + id: 0, + name: 'creation event', + timestamp: Scheduler.unstable_now(), + }; + + const onNestedUpdateScheduled = jest.fn(); + + SchedulerTracing.unstable_trace( + interactionCreation.name, + Scheduler.unstable_now(), + () => { + ReactNoop.act(() => { + ReactNoop.render( + + + , + ); + }); + }, + ); + + expect(Scheduler).toHaveYielded(['Component:false', 'Component:true']); + expect(onNestedUpdateScheduled).toHaveBeenCalledTimes(1); + if (ReactFeatureFlags.enableSchedulerTracing) { + expect(onNestedUpdateScheduled.mock.calls[0][0]).toMatchInteractions([ + interactionCreation, + ]); + } + }); + + it('is called when a class component schedules an update from the componentDidUpdate lifecycles', () => { + class Component extends React.Component { + state = { + nestedUpdateSheduled: false, + }; + componentDidUpdate(prevProps, prevState) { + if ( + this.props.scheduleNestedUpdate && + !this.state.nestedUpdateSheduled + ) { + this.setState({nestedUpdateSheduled: true}); + } + } + render() { + const {scheduleNestedUpdate} = this.props; + const {nestedUpdateSheduled} = this.state; + Scheduler.unstable_yieldValue( + `Component:${scheduleNestedUpdate}:${nestedUpdateSheduled}`, + ); + return nestedUpdateSheduled; + } + } + + const onNestedUpdateScheduled = jest.fn(); + + ReactNoop.act(() => { + ReactNoop.render( + + + , + ); + }); + expect(Scheduler).toHaveYielded(['Component:false:false']); + expect(onNestedUpdateScheduled).not.toHaveBeenCalled(); + + const interactionCreation = { + id: 0, + name: 'creation event', + timestamp: Scheduler.unstable_now(), + }; + + SchedulerTracing.unstable_trace( + interactionCreation.name, + Scheduler.unstable_now(), + () => { + ReactNoop.act(() => { + ReactNoop.render( + + + , + ); + }); + }, + ); + + expect(Scheduler).toHaveYielded([ + 'Component:true:false', + 'Component:true:true', + ]); + expect(onNestedUpdateScheduled).toHaveBeenCalledTimes(1); + if (ReactFeatureFlags.enableSchedulerTracing) { + expect(onNestedUpdateScheduled.mock.calls[0][0]).toMatchInteractions([ + interactionCreation, + ]); + } + }); + + it('is not called when a class component schedules an update outside of render', () => { + const updateFnRef = React.createRef(null); + + class Component extends React.Component { + state = { + value: false, + }; + render() { + const {value} = this.state; + updateFnRef.current = () => this.setState({value: true}); + Scheduler.unstable_yieldValue(`Component:${value}`); + return value; + } + } + + const onNestedUpdateScheduled = jest.fn(); + + ReactNoop.act(() => { + ReactNoop.render( + + + , + ); + }); + expect(Scheduler).toHaveYielded(['Component:false']); + + ReactNoop.act(() => { + updateFnRef.current(); + }); + expect(Scheduler).toHaveYielded(['Component:true']); + expect(onNestedUpdateScheduled).not.toHaveBeenCalled(); + }); + }); }); describe('interaction tracing', () => { diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index b796e5b1ffa..c2c4ada6260 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -39,6 +39,10 @@ export const enableProfilerCommitHooks = false; // Phase param passed to onRender callback differentiates between an "update" and a "cascading-update". export const enableProfilerNestedUpdatePhase = false; +// Profiler API accepts a function to be called when a nested update is scheduled. +// This callback accepts the component type (class instance or function) the update is scheduled for. +export const enableProfilerNestedUpdateScheduledHook = false; + // Trace which interactions trigger each commit. export const enableSchedulerTracing = __PROFILE__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 5a5bc9ea85f..461267619d8 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -16,6 +16,7 @@ export const enableSchedulingProfiler = false; export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = false; export const enableProfilerNestedUpdatePhase = false; +export const enableProfilerNestedUpdateScheduledHook = false; export const enableSchedulerTracing = __PROFILE__; export const enableSuspenseServerRenderer = false; export const enableSelectiveHydration = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 82c57055f1c..d3b55536eae 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -18,6 +18,7 @@ export const warnAboutDeprecatedLifecycles = true; export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = false; export const enableProfilerNestedUpdatePhase = false; +export const enableProfilerNestedUpdateScheduledHook = false; export const enableSchedulerTracing = __PROFILE__; export const enableSuspenseServerRenderer = false; export const enableSelectiveHydration = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 6a572127e5e..b20b1b1da03 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -18,6 +18,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = false; export const enableProfilerNestedUpdatePhase = false; +export const enableProfilerNestedUpdateScheduledHook = false; export const enableSchedulerTracing = __PROFILE__; export const enableSuspenseServerRenderer = false; export const enableSelectiveHydration = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index 2fae5f7e2bd..b0da73bc65f 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -18,6 +18,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = false; export const enableProfilerNestedUpdatePhase = false; +export const enableProfilerNestedUpdateScheduledHook = false; export const enableSchedulerTracing = __PROFILE__; export const enableSuspenseServerRenderer = false; export const enableSelectiveHydration = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index d254b9274ad..cfa4816166c 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -18,6 +18,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = false; export const enableProfilerNestedUpdatePhase = false; +export const enableProfilerNestedUpdateScheduledHook = false; export const enableSchedulerTracing = __PROFILE__; export const enableSuspenseServerRenderer = false; export const enableSelectiveHydration = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index 7b470ba5bf6..757e99c6e1d 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -18,6 +18,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = false; export const enableProfilerNestedUpdatePhase = false; +export const enableProfilerNestedUpdateScheduledHook = false; export const enableSchedulerTracing = __PROFILE__; export const enableSuspenseServerRenderer = false; export const enableSelectiveHydration = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index a0bc67a833b..1de76e52c3b 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -18,6 +18,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false; export const enableProfilerTimer = false; export const enableProfilerCommitHooks = false; export const enableProfilerNestedUpdatePhase = false; +export const enableProfilerNestedUpdateScheduledHook = false; export const enableSchedulerTracing = false; export const enableSuspenseServerRenderer = true; export const enableSelectiveHydration = true; diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index fee39cd71aa..95ba1859e15 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -48,3 +48,5 @@ export const disableSchedulerTimeoutBasedOnReactExpirationTime = false; export const enableDoubleInvokingEffects = false; export const enableUseRefAccessWarning = __VARIANT__; + +export const enableProfilerNestedUpdateScheduledHook = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 2e8d9d39cfa..947b05ab853 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -36,6 +36,8 @@ export const { export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = __PROFILE__; export const enableProfilerNestedUpdatePhase = __PROFILE__; +export const enableProfilerNestedUpdateScheduledHook = + __PROFILE__ && dynamicFeatureFlags.enableProfilerNestedUpdateScheduledHook; // Logs additional User Timing API marks for use with an experimental profiling tool. export const enableSchedulingProfiler = __PROFILE__;