Skip to content

Commit efd8f64

Browse files
authored
Resolve default onRecoverableError at root init (#23264)
Minor follow up to initial onRecoverableError PR. When onRecoverableError is not provided to `createRoot`, the renderer falls back to a default implementation. Originally I implemented this with a host config method, but what we can do instead is pass the default implementation the root constructor as if it were a user provided one.
1 parent e0af1aa commit efd8f64

16 files changed

+57
-55
lines changed

packages/react-art/src/ReactARTHostConfig.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,3 @@ export function preparePortalMount(portalInstance: any): void {
451451
export function detachDeletedInstance(node: Instance): void {
452452
// noop
453453
}
454-
455-
export function logRecoverableError(error) {
456-
// noop
457-
}

packages/react-dom/src/client/ReactDOMHostConfig.js

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -374,18 +374,6 @@ export function getCurrentEventPriority(): * {
374374
return getEventPriority(currentEvent.type);
375375
}
376376

377-
/* global reportError */
378-
export const logRecoverableError =
379-
typeof reportError === 'function'
380-
? // In modern browsers, reportError will dispatch an error event,
381-
// emulating an uncaught JavaScript error.
382-
reportError
383-
: (error: mixed) => {
384-
// In older browsers and test environments, fallback to console.error.
385-
// eslint-disable-next-line react-internal/no-production-logging, react-internal/warning-args
386-
console.error(error);
387-
};
388-
389377
export const isPrimaryRenderer = true;
390378
export const warnsIfNotActing = true;
391379
// This initialization code may run even on server environments

packages/react-dom/src/client/ReactDOMLegacy.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ function getReactRootElementInContainer(container: any) {
102102
}
103103
}
104104

105+
function noopOnRecoverableError() {
106+
// This isn't reachable because onRecoverableError isn't called in the
107+
// legacy API.
108+
}
109+
105110
function legacyCreateRootFromDOMContainer(
106111
container: Container,
107112
forceHydrate: boolean,
@@ -122,7 +127,7 @@ function legacyCreateRootFromDOMContainer(
122127
false, // isStrictMode
123128
false, // concurrentUpdatesByDefaultOverride,
124129
'', // identifierPrefix
125-
null,
130+
noopOnRecoverableError,
126131
);
127132
markContainerAsRoot(root.current, container);
128133

packages/react-dom/src/client/ReactDOMRoot.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,18 @@ import {
6565
import {ConcurrentRoot} from 'react-reconciler/src/ReactRootTags';
6666
import {allowConcurrentByDefault} from 'shared/ReactFeatureFlags';
6767

68+
/* global reportError */
69+
const defaultOnRecoverableError =
70+
typeof reportError === 'function'
71+
? // In modern browsers, reportError will dispatch an error event,
72+
// emulating an uncaught JavaScript error.
73+
reportError
74+
: (error: mixed) => {
75+
// In older browsers and test environments, fallback to console.error.
76+
// eslint-disable-next-line react-internal/no-production-logging, react-internal/warning-args
77+
console.error(error);
78+
};
79+
6880
function ReactDOMRoot(internalRoot: FiberRoot) {
6981
this._internalRoot = internalRoot;
7082
}
@@ -145,7 +157,7 @@ export function createRoot(
145157
let isStrictMode = false;
146158
let concurrentUpdatesByDefaultOverride = false;
147159
let identifierPrefix = '';
148-
let onRecoverableError = null;
160+
let onRecoverableError = defaultOnRecoverableError;
149161
if (options !== null && options !== undefined) {
150162
if (__DEV__) {
151163
if ((options: any).hydrate) {
@@ -220,7 +232,7 @@ export function hydrateRoot(
220232
let isStrictMode = false;
221233
let concurrentUpdatesByDefaultOverride = false;
222234
let identifierPrefix = '';
223-
let onRecoverableError = null;
235+
let onRecoverableError = defaultOnRecoverableError;
224236
if (options !== null && options !== undefined) {
225237
if (options.unstable_strictMode === true) {
226238
isStrictMode = true;

packages/react-native-renderer/src/ReactFabric.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ function sendAccessibilityEvent(handle: any, eventType: string) {
195195
}
196196
}
197197

198+
function onRecoverableError(error) {
199+
// TODO: Expose onRecoverableError option to userspace
200+
// eslint-disable-next-line react-internal/no-production-logging, react-internal/warning-args
201+
console.error(error);
202+
}
203+
198204
function render(
199205
element: Element<ElementType>,
200206
containerTag: number,
@@ -214,7 +220,7 @@ function render(
214220
false,
215221
null,
216222
'',
217-
null,
223+
onRecoverableError,
218224
);
219225
roots.set(containerTag, root);
220226
}

packages/react-native-renderer/src/ReactFabricHostConfig.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,3 @@ export function preparePortalMount(portalInstance: Instance): void {
525525
export function detachDeletedInstance(node: Instance): void {
526526
// noop
527527
}
528-
529-
export function logRecoverableError(error: mixed): void {
530-
// noop
531-
}

packages/react-native-renderer/src/ReactNativeHostConfig.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,3 @@ export function preparePortalMount(portalInstance: Instance): void {
513513
export function detachDeletedInstance(node: Instance): void {
514514
// noop
515515
}
516-
517-
export function logRecoverableError(error: mixed): void {
518-
// noop
519-
}

packages/react-native-renderer/src/ReactNativeRenderer.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,12 @@ function sendAccessibilityEvent(handle: any, eventType: string) {
192192
}
193193
}
194194

195+
function onRecoverableError(error) {
196+
// TODO: Expose onRecoverableError option to userspace
197+
// eslint-disable-next-line react-internal/no-production-logging, react-internal/warning-args
198+
console.error(error);
199+
}
200+
195201
function render(
196202
element: Element<ElementType>,
197203
containerTag: number,
@@ -210,7 +216,7 @@ function render(
210216
false,
211217
null,
212218
'',
213-
null,
219+
onRecoverableError,
214220
);
215221
roots.set(containerTag, root);
216222
}

packages/react-noop-renderer/src/createReactNoop.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,12 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
938938
return NoopRenderer.flushSync(fn);
939939
}
940940

941+
function onRecoverableError(error) {
942+
// TODO: Turn this on once tests are fixed
943+
// eslint-disable-next-line react-internal/no-production-logging, react-internal/warning-args
944+
// console.error(error);
945+
}
946+
941947
let idCounter = 0;
942948

943949
const ReactNoop = {
@@ -966,7 +972,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
966972
null,
967973
false,
968974
'',
969-
null,
975+
onRecoverableError,
970976
);
971977
roots.set(rootID, root);
972978
}
@@ -988,7 +994,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
988994
null,
989995
false,
990996
'',
991-
null,
997+
onRecoverableError,
992998
);
993999
return {
9941000
_Scheduler: Scheduler,
@@ -1018,7 +1024,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
10181024
null,
10191025
false,
10201026
'',
1021-
null,
1027+
onRecoverableError,
10221028
);
10231029
return {
10241030
_Scheduler: Scheduler,

packages/react-reconciler/src/ReactFiberReconciler.new.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ export function createContainer(
245245
isStrictMode: boolean,
246246
concurrentUpdatesByDefaultOverride: null | boolean,
247247
identifierPrefix: string,
248-
onRecoverableError: null | ((error: mixed) => void),
248+
onRecoverableError: (error: mixed) => void,
249249
): OpaqueRoot {
250250
return createFiberRoot(
251251
containerInfo,

packages/react-reconciler/src/ReactFiberReconciler.old.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ export function createContainer(
245245
isStrictMode: boolean,
246246
concurrentUpdatesByDefaultOverride: null | boolean,
247247
identifierPrefix: string,
248-
onRecoverableError: null | ((error: mixed) => void),
248+
onRecoverableError: (error: mixed) => void,
249249
): OpaqueRoot {
250250
return createFiberRoot(
251251
containerInfo,

packages/react-reconciler/src/ReactFiberWorkLoop.new.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ import {
7777
supportsMicrotasks,
7878
errorHydratingContainer,
7979
scheduleMicrotask,
80-
logRecoverableError,
8180
} from './ReactFiberHostConfig';
8281

8382
import {
@@ -2113,16 +2112,10 @@ function commitRootImpl(
21132112
if (recoverableErrors !== null) {
21142113
// There were errors during this render, but recovered from them without
21152114
// needing to surface it to the UI. We log them here.
2115+
const onRecoverableError = root.onRecoverableError;
21162116
for (let i = 0; i < recoverableErrors.length; i++) {
21172117
const recoverableError = recoverableErrors[i];
2118-
const onRecoverableError = root.onRecoverableError;
2119-
if (onRecoverableError !== null) {
2120-
onRecoverableError(recoverableError);
2121-
} else {
2122-
// No user-provided onRecoverableError. Use the default behavior
2123-
// provided by the renderer's host config.
2124-
logRecoverableError(recoverableError);
2125-
}
2118+
onRecoverableError(recoverableError);
21262119
}
21272120
}
21282121

packages/react-reconciler/src/ReactFiberWorkLoop.old.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ import {
7777
supportsMicrotasks,
7878
errorHydratingContainer,
7979
scheduleMicrotask,
80-
logRecoverableError,
8180
} from './ReactFiberHostConfig';
8281

8382
import {
@@ -2113,16 +2112,10 @@ function commitRootImpl(
21132112
if (recoverableErrors !== null) {
21142113
// There were errors during this render, but recovered from them without
21152114
// needing to surface it to the UI. We log them here.
2115+
const onRecoverableError = root.onRecoverableError;
21162116
for (let i = 0; i < recoverableErrors.length; i++) {
21172117
const recoverableError = recoverableErrors[i];
2118-
const onRecoverableError = root.onRecoverableError;
2119-
if (onRecoverableError !== null) {
2120-
onRecoverableError(recoverableError);
2121-
} else {
2122-
// No user-provided onRecoverableError. Use the default behavior
2123-
// provided by the renderer's host config.
2124-
logRecoverableError(recoverableError);
2125-
}
2118+
onRecoverableError(recoverableError);
21262119
}
21272120
}
21282121

packages/react-reconciler/src/ReactInternalTypes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ type BaseFiberRootProperties = {|
247247
// a reference to.
248248
identifierPrefix: string,
249249

250-
onRecoverableError: null | ((error: mixed) => void),
250+
onRecoverableError: (error: mixed) => void,
251251
|};
252252

253253
// The following attributes are only used by DevTools and are only present in DEV builds.

packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ export const prepareScopeUpdate = $$$hostConfig.preparePortalMount;
6868
export const getInstanceFromScope = $$$hostConfig.getInstanceFromScope;
6969
export const getCurrentEventPriority = $$$hostConfig.getCurrentEventPriority;
7070
export const detachDeletedInstance = $$$hostConfig.detachDeletedInstance;
71-
export const logRecoverableError = $$$hostConfig.logRecoverableError;
7271

7372
// -------------------
7473
// Microtasks

packages/react-test-renderer/src/ReactTestRenderer.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,12 @@ function propsMatch(props: Object, filter: Object): boolean {
437437
return true;
438438
}
439439

440+
function onRecoverableError(error) {
441+
// TODO: Expose onRecoverableError option to userspace
442+
// eslint-disable-next-line react-internal/no-production-logging, react-internal/warning-args
443+
console.error(error);
444+
}
445+
440446
function create(element: React$Element<any>, options: TestRendererOptions) {
441447
let createNodeMock = defaultTestOptions.createNodeMock;
442448
let isConcurrent = false;
@@ -472,7 +478,7 @@ function create(element: React$Element<any>, options: TestRendererOptions) {
472478
isStrictMode,
473479
concurrentUpdatesByDefault,
474480
'',
475-
null,
481+
onRecoverableError,
476482
);
477483

478484
if (root == null) {

0 commit comments

Comments
 (0)