Skip to content

Add plumbing for onDefaultTransitionIndicator #33150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/react-art/src/ReactART.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,22 @@ import {
updateContainerSync,
injectIntoDevTools,
flushSyncWork,
defaultOnUncaughtError,
defaultOnCaughtError,
defaultOnRecoverableError,
} from 'react-reconciler/src/ReactFiberReconciler';

import Transform from 'art/core/transform';
import Mode from 'art/modes/current';
import FastNoSideEffects from 'art/modes/fast-noSideEffects';
import {disableLegacyMode} from 'shared/ReactFeatureFlags';

import {TYPES, childrenAsString} from './ReactARTInternals';

function defaultOnDefaultTransitionIndicator() {
// Noop
}

Mode.setCurrent(
// Change to 'art/modes/dom' for easier debugging via SVG
FastNoSideEffects,
Expand Down Expand Up @@ -75,6 +83,11 @@ class Surface extends React.Component {
false,
false,
'',
defaultOnUncaughtError,
defaultOnCaughtError,
defaultOnRecoverableError,
Copy link
Collaborator Author

@sebmarkbage sebmarkbage May 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were missing in ART so any errors would've logged like onUncaughtError is undefined instead of the error. Probably not heavily used.

Probably should have a test for errors in ART.

defaultOnDefaultTransitionIndicator,
null,
);
// We synchronously flush updates coming from above so that they commit together
// and so that refs resolve before the parent life cycles.
Expand Down
26 changes: 25 additions & 1 deletion packages/react-dom/src/client/ReactDOMRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import type {
import {isValidContainer} from 'react-dom-bindings/src/client/ReactDOMContainer';
import {queueExplicitHydrationTarget} from 'react-dom-bindings/src/events/ReactDOMEventReplaying';
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
import {disableCommentsAsDOMContainers} from 'shared/ReactFeatureFlags';
import {
disableCommentsAsDOMContainers,
enableDefaultTransitionIndicator,
} from 'shared/ReactFeatureFlags';

export type RootType = {
render(children: ReactNodeList): void,
Expand All @@ -43,6 +46,7 @@ export type CreateRootOptions = {
error: mixed,
errorInfo: {+componentStack?: ?string},
) => void,
onDefaultTransitionIndicator?: () => void | (() => void),
};

export type HydrateRootOptions = {
Expand All @@ -68,6 +72,7 @@ export type HydrateRootOptions = {
error: mixed,
errorInfo: {+componentStack?: ?string},
) => void,
onDefaultTransitionIndicator?: () => void | (() => void),
formState?: ReactFormState<any, any> | null,
};

Expand All @@ -92,6 +97,11 @@ import {
} from 'react-reconciler/src/ReactFiberReconciler';
import {ConcurrentRoot} from 'react-reconciler/src/ReactRootTags';

function defaultOnDefaultTransitionIndicator(): void | (() => void) {
// TODO: Implement the default
return function () {};
}

// $FlowFixMe[missing-this-annot]
function ReactDOMRoot(internalRoot: FiberRoot) {
this._internalRoot = internalRoot;
Expand Down Expand Up @@ -178,6 +188,7 @@ export function createRoot(
let onUncaughtError = defaultOnUncaughtError;
let onCaughtError = defaultOnCaughtError;
let onRecoverableError = defaultOnRecoverableError;
let onDefaultTransitionIndicator = defaultOnDefaultTransitionIndicator;
let transitionCallbacks = null;

if (options !== null && options !== undefined) {
Expand Down Expand Up @@ -217,6 +228,11 @@ export function createRoot(
if (options.onRecoverableError !== undefined) {
onRecoverableError = options.onRecoverableError;
}
if (enableDefaultTransitionIndicator) {
if (options.onDefaultTransitionIndicator !== undefined) {
onDefaultTransitionIndicator = options.onDefaultTransitionIndicator;
}
}
if (options.unstable_transitionCallbacks !== undefined) {
transitionCallbacks = options.unstable_transitionCallbacks;
}
Expand All @@ -232,6 +248,7 @@ export function createRoot(
onUncaughtError,
onCaughtError,
onRecoverableError,
onDefaultTransitionIndicator,
transitionCallbacks,
);
markContainerAsRoot(root.current, container);
Expand Down Expand Up @@ -288,6 +305,7 @@ export function hydrateRoot(
let onUncaughtError = defaultOnUncaughtError;
let onCaughtError = defaultOnCaughtError;
let onRecoverableError = defaultOnRecoverableError;
let onDefaultTransitionIndicator = defaultOnDefaultTransitionIndicator;
let transitionCallbacks = null;
let formState = null;
if (options !== null && options !== undefined) {
Expand All @@ -306,6 +324,11 @@ export function hydrateRoot(
if (options.onRecoverableError !== undefined) {
onRecoverableError = options.onRecoverableError;
}
if (enableDefaultTransitionIndicator) {
if (options.onDefaultTransitionIndicator !== undefined) {
onDefaultTransitionIndicator = options.onDefaultTransitionIndicator;
}
}
if (options.unstable_transitionCallbacks !== undefined) {
transitionCallbacks = options.unstable_transitionCallbacks;
}
Expand All @@ -326,6 +349,7 @@ export function hydrateRoot(
onUncaughtError,
onCaughtError,
onRecoverableError,
onDefaultTransitionIndicator,
transitionCallbacks,
formState,
);
Expand Down
6 changes: 6 additions & 0 deletions packages/react-dom/src/client/ReactDOMRootFB.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ function noopOnRecoverableError() {
// legacy API.
}

function noopOnDefaultTransitionIndicator() {
// Noop
}

function legacyCreateRootFromDOMContainer(
container: Container,
initialChildren: ReactNodeList,
Expand Down Expand Up @@ -239,6 +243,7 @@ function legacyCreateRootFromDOMContainer(
wwwOnUncaughtError,
wwwOnCaughtError,
noopOnRecoverableError,
noopOnDefaultTransitionIndicator,
// TODO(luna) Support hydration later
null,
null,
Expand Down Expand Up @@ -277,6 +282,7 @@ function legacyCreateRootFromDOMContainer(
wwwOnUncaughtError,
wwwOnCaughtError,
noopOnRecoverableError,
noopOnDefaultTransitionIndicator,
null, // transitionCallbacks
);
container._reactRootContainer = root;
Expand Down
4 changes: 4 additions & 0 deletions packages/react-native-renderer/src/ReactFabric.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ function nativeOnCaughtError(

defaultOnCaughtError(error, errorInfo);
}
function nativeOnDefaultTransitionIndicator(): void | (() => void) {
// Native doesn't have a default indicator.
}

function render(
element: Element<ElementType>,
Expand Down Expand Up @@ -148,6 +151,7 @@ function render(
onUncaughtError,
onCaughtError,
onRecoverableError,
nativeOnDefaultTransitionIndicator,
null,
);

Expand Down
4 changes: 4 additions & 0 deletions packages/react-native-renderer/src/ReactNativeRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ function nativeOnCaughtError(

defaultOnCaughtError(error, errorInfo);
}
function nativeOnDefaultTransitionIndicator(): void | (() => void) {
// Native doesn't have a default indicator.
}

function render(
element: MixedElement,
Expand Down Expand Up @@ -162,6 +165,7 @@ function render(
onUncaughtError,
onCaughtError,
onRecoverableError,
nativeOnDefaultTransitionIndicator,
null,
);
roots.set(containerTag, root);
Expand Down
9 changes: 9 additions & 0 deletions packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type CreateRootOptions = {
unstable_transitionCallbacks?: TransitionTracingCallbacks,
onUncaughtError?: (error: mixed, errorInfo: {componentStack: string}) => void,
onCaughtError?: (error: mixed, errorInfo: {componentStack: string}) => void,
onDefaultTransitionIndicator?: () => void | (() => void),
...
};
type InstanceMeasurement = null;
Expand Down Expand Up @@ -1141,6 +1142,9 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
// TODO: Turn this on once tests are fixed
// console.error(error);
}
function onDefaultTransitionIndicator(): void | (() => void) {
// TODO: Allow this as an option.
}

let idCounter = 0;

Expand Down Expand Up @@ -1196,6 +1200,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
NoopRenderer.defaultOnUncaughtError,
NoopRenderer.defaultOnCaughtError,
onRecoverableError,
onDefaultTransitionIndicator,
null,
);
roots.set(rootID, root);
Expand Down Expand Up @@ -1224,6 +1229,9 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
? options.onCaughtError
: NoopRenderer.defaultOnCaughtError,
onRecoverableError,
options && options.onDefaultTransitionIndicator
? options.onDefaultTransitionIndicator
: onDefaultTransitionIndicator,
options && options.unstable_transitionCallbacks
? options.unstable_transitionCallbacks
: null,
Expand Down Expand Up @@ -1262,6 +1270,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
NoopRenderer.defaultOnUncaughtError,
NoopRenderer.defaultOnCaughtError,
onRecoverableError,
onDefaultTransitionIndicator,
null,
);
return {
Expand Down
8 changes: 6 additions & 2 deletions packages/react-reconciler/src/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ export function createContainer(
error: mixed,
errorInfo: {+componentStack?: ?string},
) => void,
onDefaultTransitionIndicator: () => void | (() => void),
transitionCallbacks: null | TransitionTracingCallbacks,
): OpaqueRoot {
const hydrate = false;
Expand All @@ -266,11 +267,12 @@ export function createContainer(
hydrationCallbacks,
isStrictMode,
identifierPrefix,
null,
onUncaughtError,
onCaughtError,
onRecoverableError,
onDefaultTransitionIndicator,
transitionCallbacks,
null,
);
}

Expand Down Expand Up @@ -300,6 +302,7 @@ export function createHydrationContainer(
error: mixed,
errorInfo: {+componentStack?: ?string},
) => void,
onDefaultTransitionIndicator: () => void | (() => void),
transitionCallbacks: null | TransitionTracingCallbacks,
formState: ReactFormState<any, any> | null,
): OpaqueRoot {
Expand All @@ -312,11 +315,12 @@ export function createHydrationContainer(
hydrationCallbacks,
isStrictMode,
identifierPrefix,
formState,
onUncaughtError,
onCaughtError,
onRecoverableError,
onDefaultTransitionIndicator,
transitionCallbacks,
formState,
);

// TODO: Move this to FiberRoot constructor
Expand Down
10 changes: 9 additions & 1 deletion packages/react-reconciler/src/ReactFiberRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
disableLegacyMode,
enableViewTransition,
enableGestureTransition,
enableDefaultTransitionIndicator,
} from 'shared/ReactFeatureFlags';
import {initializeUpdateQueue} from './ReactFiberClassUpdateQueue';
import {LegacyRoot, ConcurrentRoot} from './ReactRootTags';
Expand All @@ -56,6 +57,7 @@ function FiberRootNode(
onUncaughtError: any,
onCaughtError: any,
onRecoverableError: any,
onDefaultTransitionIndicator: any,
formState: ReactFormState<any, any> | null,
) {
this.tag = disableLegacyMode ? ConcurrentRoot : tag;
Expand Down Expand Up @@ -90,6 +92,10 @@ function FiberRootNode(
this.onCaughtError = onCaughtError;
this.onRecoverableError = onRecoverableError;

if (enableDefaultTransitionIndicator) {
this.onDefaultTransitionIndicator = onDefaultTransitionIndicator;
}

this.pooledCache = null;
this.pooledCacheLanes = NoLanes;

Expand Down Expand Up @@ -157,6 +163,7 @@ export function createFiberRoot(
// them through the root constructor. Perhaps we should put them all into a
// single type, like a DynamicHostConfig that is defined by the renderer.
identifierPrefix: string,
formState: ReactFormState<any, any> | null,
onUncaughtError: (
error: mixed,
errorInfo: {+componentStack?: ?string},
Expand All @@ -172,8 +179,8 @@ export function createFiberRoot(
error: mixed,
errorInfo: {+componentStack?: ?string},
) => void,
onDefaultTransitionIndicator: () => void | (() => void),
transitionCallbacks: null | TransitionTracingCallbacks,
formState: ReactFormState<any, any> | null,
): FiberRoot {
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
const root: FiberRoot = (new FiberRootNode(
Expand All @@ -184,6 +191,7 @@ export function createFiberRoot(
onUncaughtError,
onCaughtError,
onRecoverableError,
onDefaultTransitionIndicator,
formState,
): any);
if (enableSuspenseCallback) {
Expand Down
2 changes: 2 additions & 0 deletions packages/react-reconciler/src/ReactInternalTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ type BaseFiberRootProperties = {
errorInfo: {+componentStack?: ?string},
) => void,

onDefaultTransitionIndicator: () => void | (() => void),

formState: ReactFormState<any, any> | null,

// enableViewTransition only
Expand Down
5 changes: 5 additions & 0 deletions packages/react-test-renderer/src/ReactTestRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ import {
disableLegacyMode,
} from 'shared/ReactFeatureFlags';

function defaultOnDefaultTransitionIndicator(): void | (() => void) {
// Noop
}

// $FlowFixMe[prop-missing]: This is only in the development export.
const act = React.act;

Expand Down Expand Up @@ -515,6 +519,7 @@ function create(
defaultOnUncaughtError,
defaultOnCaughtError,
defaultOnRecoverableError,
defaultOnDefaultTransitionIndicator,
null,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ describe('ReactTestRenderer', () => {
expect.anything(),
expect.anything(),
expect.anything(),
expect.anything(),
null,
);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/ReactFeatureFlags.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ export const enableSrcObject = __EXPERIMENTAL__;

export const enableHydrationChangeEvent = __EXPERIMENTAL__;

export const enableDefaultTransitionIndicator = __EXPERIMENTAL__;

/**
* Switches Fiber creation to a simple object instead of a constructor.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export const enableScrollEndPolyfill = true;
export const enableSuspenseyImages = false;
export const enableSrcObject = false;
export const enableHydrationChangeEvent = true;
export const enableDefaultTransitionIndicator = false;
export const ownerStackLimit = 1e4;

// Flow magic to verify the exports of this file match the original version.
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const enableScrollEndPolyfill = true;
export const enableSuspenseyImages = false;
export const enableSrcObject = false;
export const enableHydrationChangeEvent = false;
export const enableDefaultTransitionIndicator = false;
export const ownerStackLimit = 1e4;

export const enableFragmentRefs = false;
Expand Down
Loading
Loading