Skip to content

Commit 0347fcd

Browse files
authored
Add on(Caught|Uncaught|Recoverable) opts to RN (#28836)
## Overview There's currently a bug in RN now that we no longer re-throw errors. The `showErrorDialog` function in React Native only logs the errors as soft errors, and never a fatal. RN was depending on the global handler for the fatal error handling and logging. Instead of fixing this in `ReactFiberErrorDialog`, we can implement the new root options in RN to handle caught/uncaught/recoverable in the respective functions, and delete ReactFiberErrorDialog. I'll follow up with a RN PR to implement these options and fix the error handling.
1 parent c113503 commit 0347fcd

File tree

2 files changed

+78
-6
lines changed

2 files changed

+78
-6
lines changed

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

+39-3
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,30 @@ function nativeOnCaughtError(
101101
defaultOnCaughtError(error, errorInfo);
102102
}
103103

104+
type NativeRenderOptions = {
105+
onUncaughtError?: (
106+
error: mixed,
107+
errorInfo: {+componentStack?: ?string},
108+
) => void,
109+
onCaughtError?: (
110+
error: mixed,
111+
errorInfo: {
112+
+componentStack?: ?string,
113+
+errorBoundary?: ?React$Component<any, any>,
114+
},
115+
) => void,
116+
onRecoverableError?: (
117+
error: mixed,
118+
errorInfo: {+componentStack?: ?string},
119+
) => void,
120+
};
121+
104122
function render(
105123
element: Element<ElementType>,
106124
containerTag: number,
107125
callback: ?() => void,
108126
concurrentRoot: ?boolean,
127+
options?: NativeRenderOptions,
109128
): ?ElementRef<ElementType> {
110129
if (disableLegacyMode && !concurrentRoot) {
111130
throw new Error('render: Unsupported Legacy Mode API.');
@@ -114,6 +133,23 @@ function render(
114133
let root = roots.get(containerTag);
115134

116135
if (!root) {
136+
// TODO: these defaults are for backwards compatibility.
137+
// Once RN implements these options internally,
138+
// we can remove the defaults and ReactFiberErrorDialog.
139+
let onUncaughtError = nativeOnUncaughtError;
140+
let onCaughtError = nativeOnCaughtError;
141+
let onRecoverableError = defaultOnRecoverableError;
142+
143+
if (options && options.onUncaughtError !== undefined) {
144+
onUncaughtError = options.onUncaughtError;
145+
}
146+
if (options && options.onCaughtError !== undefined) {
147+
onCaughtError = options.onCaughtError;
148+
}
149+
if (options && options.onRecoverableError !== undefined) {
150+
onRecoverableError = options.onRecoverableError;
151+
}
152+
117153
// TODO (bvaughn): If we decide to keep the wrapper component,
118154
// We could create a wrapper for containerTag as well to reduce special casing.
119155
root = createContainer(
@@ -123,9 +159,9 @@ function render(
123159
false,
124160
null,
125161
'',
126-
nativeOnUncaughtError,
127-
nativeOnCaughtError,
128-
defaultOnRecoverableError,
162+
onUncaughtError,
163+
onCaughtError,
164+
onRecoverableError,
129165
null,
130166
);
131167
roots.set(containerTag, root);

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

+39-3
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,29 @@ function nativeOnCaughtError(
106106
defaultOnCaughtError(error, errorInfo);
107107
}
108108

109+
type NativeRenderOptions = {
110+
onUncaughtError?: (
111+
error: mixed,
112+
errorInfo: {+componentStack?: ?string},
113+
) => void,
114+
onCaughtError?: (
115+
error: mixed,
116+
errorInfo: {
117+
+componentStack?: ?string,
118+
+errorBoundary?: ?React$Component<any, any>,
119+
},
120+
) => void,
121+
onRecoverableError?: (
122+
error: mixed,
123+
errorInfo: {+componentStack?: ?string},
124+
) => void,
125+
};
126+
109127
function render(
110128
element: Element<ElementType>,
111129
containerTag: number,
112130
callback: ?() => void,
131+
options?: NativeRenderOptions,
113132
): ?ElementRef<ElementType> {
114133
if (disableLegacyMode) {
115134
throw new Error('render: Unsupported Legacy Mode API.');
@@ -118,6 +137,23 @@ function render(
118137
let root = roots.get(containerTag);
119138

120139
if (!root) {
140+
// TODO: these defaults are for backwards compatibility.
141+
// Once RN implements these options internally,
142+
// we can remove the defaults and ReactFiberErrorDialog.
143+
let onUncaughtError = nativeOnUncaughtError;
144+
let onCaughtError = nativeOnCaughtError;
145+
let onRecoverableError = defaultOnRecoverableError;
146+
147+
if (options && options.onUncaughtError !== undefined) {
148+
onUncaughtError = options.onUncaughtError;
149+
}
150+
if (options && options.onCaughtError !== undefined) {
151+
onCaughtError = options.onCaughtError;
152+
}
153+
if (options && options.onRecoverableError !== undefined) {
154+
onRecoverableError = options.onRecoverableError;
155+
}
156+
121157
// TODO (bvaughn): If we decide to keep the wrapper component,
122158
// We could create a wrapper for containerTag as well to reduce special casing.
123159
root = createContainer(
@@ -127,9 +163,9 @@ function render(
127163
false,
128164
null,
129165
'',
130-
nativeOnUncaughtError,
131-
nativeOnCaughtError,
132-
defaultOnRecoverableError,
166+
onUncaughtError,
167+
onCaughtError,
168+
onRecoverableError,
133169
null,
134170
);
135171
roots.set(containerTag, root);

0 commit comments

Comments
 (0)