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 9153c03049b9f..4ccd2ea67e8b5 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 b2476dad5b615..609bd8016b9cb 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 d9c70ae73b75c..92b30d9280fab 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 b796e5b1ffacc..c2c4ada6260de 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 5a5bc9ea85f80..461267619d823 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 82c57055f1c0c..d3b55536eaef7 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 6a572127e5ea2..b20b1b1da03e0 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 2fae5f7e2bdaa..b0da73bc65fb8 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 d254b9274ad3a..cfa4816166c0c 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 7b470ba5bf674..757e99c6e1dc9 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 a0bc67a833bda..1de76e52c3bb1 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 fee39cd71aa41..95ba1859e1507 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 2e8d9d39cfa43..947b05ab85302 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__;