@samitouri / QOS-React-2 / commits / 3553489f7b

Fix now-missing errorInfo argument to componentDidCatch() (#12416)

* Add a failing test verifying componentInfo is missing * Pass componentInfo to componentDidCatch and getDerivedStateFromCatch * Only expect stack in DEV * Don't pass the stack to getDerivedStateFromCatch()

Dan Abramov committed Mar 21, 2018 at 16:38 UTC 3553489f7b26fc3cb952321dd21c0b828a375f55
2 files changed +78 -1
packages/react-reconciler/src/ReactFiberCommitWork.js
+4 -1
@@ -270,8 +270,11 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
270 for (let i = 0; i < capturedErrors.length; i++) {
271 const errorInfo = capturedErrors[i];
272 const error = errorInfo.value;
273 + const stack = errorInfo.stack;
274 logError(finishedWork, errorInfo);
274 - instance.componentDidCatch(error);
275 + instance.componentDidCatch(error, {
276 + componentStack: stack !== null ? stack : '',
277 + });
278 }
279 }
280 break;
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+74
@@ -36,6 +36,10 @@ describe('ReactIncrementalErrorHandling', () => {
36 return {type: 'span', children: [], prop};
37 }
38
39 + function normalizeCodeLocInfo(str) {
40 + return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
41 + }
42 +
43 it('recovers from errors asynchronously', () => {
44 class ErrorBoundary extends React.Component {
45 state = {error: null};
@@ -1302,4 +1306,74 @@ describe('ReactIncrementalErrorHandling', () => {
1306 ]);
1307 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]);
1308 });
1309 +
1310 + it('provides component stack to the error boundary with componentDidCatch', () => {
1311 + class ErrorBoundary extends React.Component {
1312 + state = {error: null, errorInfo: null};
1313 + componentDidCatch(error, errorInfo) {
1314 + this.setState({error, errorInfo});
1315 + }
1316 + render() {
1317 + if (this.state.errorInfo) {
1318 + ReactNoop.yield('render error message');
1319 + return (
1320 + <span
1321 + prop={`Caught an error:${normalizeCodeLocInfo(
1322 + this.state.errorInfo.componentStack,
1323 + )}.`}
1324 + />
1325 + );
1326 + }
1327 + return this.props.children;
1328 + }
1329 + }
1330 +
1331 + function BrokenRender(props) {
1332 + throw new Error('Hello');
1333 + }
1334 +
1335 + ReactNoop.render(
1336 + <ErrorBoundary>
1337 + <BrokenRender />
1338 + </ErrorBoundary>,
1339 + );
1340 + ReactNoop.flushDeferredPri();
1341 + expect(ReactNoop.getChildren()).toEqual([
1342 + span(
1343 + 'Caught an error:\n' +
1344 + (__DEV__
1345 + ? ' in BrokenRender (at **)\n'
1346 + : ' in BrokenRender\n') +
1347 + (__DEV__ ? ' in ErrorBoundary (at **).' : ' in ErrorBoundary.'),
1348 + ),
1349 + ]);
1350 + });
1351 +
1352 + it('does not provide component stack to the error boundary with getDerivedStateFromCatch', () => {
1353 + class ErrorBoundary extends React.Component {
1354 + state = {error: null};
1355 + static getDerivedStateFromCatch(error, errorInfo) {
1356 + expect(errorInfo).toBeUndefined();
1357 + return {error};
1358 + }
1359 + render() {
1360 + if (this.state.error) {
1361 + return <span prop={`Caught an error: ${this.state.error.message}`} />;
1362 + }
1363 + return this.props.children;
1364 + }
1365 + }
1366 +
1367 + function BrokenRender(props) {
1368 + throw new Error('Hello');
1369 + }
1370 +
1371 + ReactNoop.render(
1372 + <ErrorBoundary>
1373 + <BrokenRender />
1374 + </ErrorBoundary>,
1375 + );
1376 + ReactNoop.flushDeferredPri();
1377 + expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello')]);
1378 + });
1379 });