diff --git a/packages/react-dom/src/client/ReactDOMLegacy.js b/packages/react-dom/src/client/ReactDOMLegacy.js index b95be0bd3f22d..340e151bf893c 100644 --- a/packages/react-dom/src/client/ReactDOMLegacy.js +++ b/packages/react-dom/src/client/ReactDOMLegacy.js @@ -127,7 +127,8 @@ function legacyCreateRootFromDOMContainer( false, // isStrictMode false, // concurrentUpdatesByDefaultOverride, '', // identifierPrefix - noopOnRecoverableError, + noopOnRecoverableError, // onRecoverableError + null, // transitionCallbacks ); markContainerAsRoot(root.current, container); diff --git a/packages/react-dom/src/client/ReactDOMRoot.js b/packages/react-dom/src/client/ReactDOMRoot.js index 9867a1b45e5da..1fe4557cb8bc1 100644 --- a/packages/react-dom/src/client/ReactDOMRoot.js +++ b/packages/react-dom/src/client/ReactDOMRoot.js @@ -9,7 +9,10 @@ import type {Container} from './ReactDOMHostConfig'; import type {MutableSource, ReactNodeList} from 'shared/ReactTypes'; -import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes'; +import type { + FiberRoot, + TransitionTracingCallbacks, +} from 'react-reconciler/src/ReactInternalTypes'; import {queueExplicitHydrationTarget} from '../events/ReactDOMEventReplaying'; @@ -25,6 +28,7 @@ export type CreateRootOptions = { unstable_concurrentUpdatesByDefault?: boolean, identifierPrefix?: string, onRecoverableError?: (error: mixed) => void, + transitionCallbacks?: TransitionTracingCallbacks, ... }; @@ -158,6 +162,8 @@ export function createRoot( let concurrentUpdatesByDefaultOverride = false; let identifierPrefix = ''; let onRecoverableError = defaultOnRecoverableError; + let transitionCallbacks = null; + if (options !== null && options !== undefined) { if (__DEV__) { if ((options: any).hydrate) { @@ -181,6 +187,9 @@ export function createRoot( if (options.onRecoverableError !== undefined) { onRecoverableError = options.onRecoverableError; } + if (options.transitionCallbacks !== undefined) { + transitionCallbacks = options.transitionCallbacks; + } } const root = createContainer( @@ -192,6 +201,7 @@ export function createRoot( concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, + transitionCallbacks, ); markContainerAsRoot(root.current, container); @@ -260,6 +270,8 @@ export function hydrateRoot( concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, + // TODO(luna) Support hydration later + null, ); markContainerAsRoot(root.current, container); // This can't be a comment node since hydration doesn't work on comment nodes anyway. diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index c314af59cb59a..e0ba72076a1af 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -14,7 +14,10 @@ * environment. */ -import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; +import type { + Fiber, + TransitionTracingCallbacks, +} from 'react-reconciler/src/ReactInternalTypes'; import type {UpdateQueue} from 'react-reconciler/src/ReactUpdateQueue'; import type {ReactNodeList, OffscreenMode} from 'shared/ReactTypes'; import type {RootTag} from 'react-reconciler/src/ReactRootTags'; @@ -64,6 +67,10 @@ type TextInstance = {| context: HostContext, |}; type HostContext = Object; +type CreateRootOptions = { + transitionCallbacks?: TransitionTracingCallbacks, + ... +}; const NO_CONTEXT = {}; const UPPERCASE_CONTEXT = {}; @@ -980,7 +987,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { }, // TODO: Replace ReactNoop.render with createRoot + root.render - createRoot() { + createRoot(options?: CreateRootOptions) { const container = { rootID: '' + idCounter++, pendingChildren: [], @@ -995,6 +1002,9 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { false, '', onRecoverableError, + options && options.transitionCallbacks + ? options.transitionCallbacks + : null, ); return { _Scheduler: Scheduler, diff --git a/packages/react-reconciler/src/ReactFiberReconciler.new.js b/packages/react-reconciler/src/ReactFiberReconciler.new.js index d3130581d2ff8..6db63352bfc25 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.new.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.new.js @@ -7,7 +7,11 @@ * @flow */ -import type {Fiber, SuspenseHydrationCallbacks} from './ReactInternalTypes'; +import type { + Fiber, + SuspenseHydrationCallbacks, + TransitionTracingCallbacks, +} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; import type {RootTag} from './ReactRootTags'; import type { @@ -246,6 +250,7 @@ export function createContainer( concurrentUpdatesByDefaultOverride: null | boolean, identifierPrefix: string, onRecoverableError: (error: mixed) => void, + transitionCallbacks: null | TransitionTracingCallbacks, ): OpaqueRoot { return createFiberRoot( containerInfo, @@ -256,6 +261,7 @@ export function createContainer( concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, + transitionCallbacks, ); } diff --git a/packages/react-reconciler/src/ReactFiberReconciler.old.js b/packages/react-reconciler/src/ReactFiberReconciler.old.js index 19136883c778c..d8be40ba02673 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.old.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.old.js @@ -7,7 +7,11 @@ * @flow */ -import type {Fiber, SuspenseHydrationCallbacks} from './ReactInternalTypes'; +import type { + Fiber, + SuspenseHydrationCallbacks, + TransitionTracingCallbacks, +} from './ReactInternalTypes'; import type {FiberRoot} from './ReactInternalTypes'; import type {RootTag} from './ReactRootTags'; import type { @@ -246,6 +250,7 @@ export function createContainer( concurrentUpdatesByDefaultOverride: null | boolean, identifierPrefix: string, onRecoverableError: (error: mixed) => void, + transitionCallbacks: null | TransitionTracingCallbacks, ): OpaqueRoot { return createFiberRoot( containerInfo, @@ -256,6 +261,7 @@ export function createContainer( concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, + transitionCallbacks, ); } diff --git a/packages/react-reconciler/src/ReactFiberRoot.new.js b/packages/react-reconciler/src/ReactFiberRoot.new.js index 85820dc7a3cdf..3a4546008ad50 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.new.js +++ b/packages/react-reconciler/src/ReactFiberRoot.new.js @@ -7,7 +7,11 @@ * @flow */ -import type {FiberRoot, SuspenseHydrationCallbacks} from './ReactInternalTypes'; +import type { + FiberRoot, + SuspenseHydrationCallbacks, + TransitionTracingCallbacks, +} from './ReactInternalTypes'; import type {RootTag} from './ReactRootTags'; import {noTimeout, supportsHydration} from './ReactFiberHostConfig'; @@ -25,6 +29,7 @@ import { enableProfilerCommitHooks, enableProfilerTimer, enableUpdaterTracking, + enableTransitionTracing, } from 'shared/ReactFeatureFlags'; import {initializeUpdateQueue} from './ReactUpdateQueue.new'; import {LegacyRoot, ConcurrentRoot} from './ReactRootTags'; @@ -78,6 +83,10 @@ function FiberRootNode( this.hydrationCallbacks = null; } + if (enableTransitionTracing) { + this.transitionCallbacks = null; + } + if (enableProfilerTimer && enableProfilerCommitHooks) { this.effectDuration = 0; this.passiveEffectDuration = 0; @@ -116,6 +125,7 @@ export function createFiberRoot( // single type, like a DynamicHostConfig that is defined by the renderer. identifierPrefix: string, onRecoverableError: null | ((error: mixed) => void), + transitionCallbacks: null | TransitionTracingCallbacks, ): FiberRoot { const root: FiberRoot = (new FiberRootNode( containerInfo, @@ -128,6 +138,10 @@ export function createFiberRoot( root.hydrationCallbacks = hydrationCallbacks; } + if (enableTransitionTracing) { + root.transitionCallbacks = transitionCallbacks; + } + // Cyclic construction. This cheats the type system right now because // stateNode is any. const uninitializedFiber = createHostRootFiber( diff --git a/packages/react-reconciler/src/ReactFiberRoot.old.js b/packages/react-reconciler/src/ReactFiberRoot.old.js index e1eaee798bfb2..56ebcd5cccac4 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.old.js +++ b/packages/react-reconciler/src/ReactFiberRoot.old.js @@ -7,7 +7,11 @@ * @flow */ -import type {FiberRoot, SuspenseHydrationCallbacks} from './ReactInternalTypes'; +import type { + FiberRoot, + SuspenseHydrationCallbacks, + TransitionTracingCallbacks, +} from './ReactInternalTypes'; import type {RootTag} from './ReactRootTags'; import {noTimeout, supportsHydration} from './ReactFiberHostConfig'; @@ -25,6 +29,7 @@ import { enableProfilerCommitHooks, enableProfilerTimer, enableUpdaterTracking, + enableTransitionTracing, } from 'shared/ReactFeatureFlags'; import {initializeUpdateQueue} from './ReactUpdateQueue.old'; import {LegacyRoot, ConcurrentRoot} from './ReactRootTags'; @@ -78,6 +83,10 @@ function FiberRootNode( this.hydrationCallbacks = null; } + if (enableTransitionTracing) { + this.transitionCallbacks = null; + } + if (enableProfilerTimer && enableProfilerCommitHooks) { this.effectDuration = 0; this.passiveEffectDuration = 0; @@ -116,6 +125,7 @@ export function createFiberRoot( // single type, like a DynamicHostConfig that is defined by the renderer. identifierPrefix: string, onRecoverableError: null | ((error: mixed) => void), + transitionCallbacks: null | TransitionTracingCallbacks, ): FiberRoot { const root: FiberRoot = (new FiberRootNode( containerInfo, @@ -128,6 +138,10 @@ export function createFiberRoot( root.hydrationCallbacks = hydrationCallbacks; } + if (enableTransitionTracing) { + root.transitionCallbacks = transitionCallbacks; + } + // Cyclic construction. This cheats the type system right now because // stateNode is any. const uninitializedFiber = createHostRootFiber( diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 352498be61f30..ed668d9126b6f 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -268,6 +268,60 @@ type SuspenseCallbackOnlyFiberRootProperties = {| hydrationCallbacks: null | SuspenseHydrationCallbacks, |}; +export type TransitionTracingCallbacks = { + onTransitionStart?: (transitionName: string, startTime: number) => void, + onTransitionProgress?: ( + transitionName: string, + startTime: number, + currentTime: number, + pending: Array<{name: null | string}>, + ) => void, + onTransitionIncomplete?: ( + transitionName: string, + startTime: number, + deletions: Array<{ + type: string, + name?: string, + newName?: string, + endTime: number, + }>, + ) => void, + onTransitionComplete?: ( + transitionName: string, + startTime: number, + endTime: number, + ) => void, + onMarkerProgress?: ( + transitionName: string, + marker: string, + startTime: number, + currentTime: number, + pending: Array<{name: null | string}>, + ) => void, + onMarkerIncomplete?: ( + transitionName: string, + marker: string, + startTime: number, + deletions: Array<{ + type: string, + name?: string, + newName?: string, + endTime: number, + }>, + ) => void, + onMarkerComplete?: ( + transitionName: string, + marker: string, + startTime: number, + endTime: number, + ) => void, +}; + +// The following fields are only used in transition tracing in Profile builds +type TransitionTracingOnlyFiberRootProperties = {| + transitionCallbacks: null | TransitionTracingCallbacks, +|}; + // Exported FiberRoot type includes all properties, // To avoid requiring potentially error-prone :any casts throughout the project. // The types are defined separately within this file to ensure they stay in sync. @@ -275,6 +329,7 @@ export type FiberRoot = { ...BaseFiberRootProperties, ...SuspenseCallbackOnlyFiberRootProperties, ...UpdaterTrackingOnlyFiberRootProperties, + ...TransitionTracingOnlyFiberRootProperties, ... };