diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 4d8e677e03..225d3657e0 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -92,7 +92,7 @@ describe('ReactDOMFizzServer', () => { function expectErrors(errorsArr, toBeDevArr, toBeProdArr) { const mappedErrows = errorsArr.map(({error, errorInfo}) => { const stack = errorInfo && errorInfo.componentStack; - const digest = errorInfo && errorInfo.digest; + const digest = error.digest; if (stack) { return [error.message, digest, normalizeCodeLocInfo(stack)]; } else if (digest) { @@ -3230,6 +3230,47 @@ describe('ReactDOMFizzServer', () => { ); }); + it('warns in dev if you access digest from errorInfo in onRecoverableError', async () => { + await act(async () => { + const {pipe} = ReactDOMFizzServer.renderToPipeableStream( +
+ + + +
, + { + onError(error) { + return 'a digest'; + }, + }, + ); + rejectText('hello'); + pipe(writable); + }); + expect(getVisibleChildren(container)).toEqual(
loading...
); + + ReactDOMClient.hydrateRoot( + container, +
+ hello +
, + { + onRecoverableError(error, errorInfo) { + expect(() => { + expect(error.digest).toBe('a digest'); + expect(errorInfo.digest).toBe('a digest'); + }).toErrorDev( + 'Warning: You are accessing "digest" from the errorInfo object passed to onRecoverableError.' + + ' This property is deprecated and will be removed in a future version of React.' + + ' To access the digest of an Error look for this property on the Error instance itself.', + {withoutStack: true}, + ); + }, + }, + ); + expect(Scheduler).toFlushWithoutYielding(); + }); + describe('error escaping', () => { it('escapes error hash, message, and component stack values in directly flushed errors (html escaping)', async () => { window.__outlet = {}; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 913a054be2..c18bed4d80 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -2746,6 +2746,7 @@ function updateDehydratedSuspenseComponent( 'client rendering.', ); } + (error: any).digest = digest; const capturedValue = createCapturedValue(error, digest, stack); return retrySuspenseComponentWithoutHydrating( current, diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 382e907571..7b1f5b9c21 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -2746,6 +2746,7 @@ function updateDehydratedSuspenseComponent( 'client rendering.', ); } + (error: any).digest = digest; const capturedValue = createCapturedValue(error, digest, stack); return retrySuspenseComponentWithoutHydrating( current, diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 0e4564538e..6d24463086 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -2596,9 +2596,11 @@ function commitRootImpl( const onRecoverableError = root.onRecoverableError; for (let i = 0; i < recoverableErrors.length; i++) { const recoverableError = recoverableErrors[i]; - const componentStack = recoverableError.stack; - const digest = recoverableError.digest; - onRecoverableError(recoverableError.value, {componentStack, digest}); + const errorInfo = makeErrorInfo( + recoverableError.digest, + recoverableError.stack, + ); + onRecoverableError(recoverableError.value, errorInfo); } } @@ -2689,6 +2691,33 @@ function commitRootImpl( return null; } +function makeErrorInfo(digest: ?string, componentStack: ?string) { + if (__DEV__) { + const errorInfo = { + componentStack, + digest, + }; + Object.defineProperty(errorInfo, 'digest', { + configurable: false, + enumerable: true, + get() { + console.error( + 'You are accessing "digest" from the errorInfo object passed to onRecoverableError.' + + ' This property is deprecated and will be removed in a future version of React.' + + ' To access the digest of an Error look for this property on the Error instance itself.', + ); + return digest; + }, + }); + return errorInfo; + } else { + return { + digest, + componentStack, + }; + } +} + function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) { if (enableCache) { const pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 496d3f91d5..36799208dc 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -2596,9 +2596,11 @@ function commitRootImpl( const onRecoverableError = root.onRecoverableError; for (let i = 0; i < recoverableErrors.length; i++) { const recoverableError = recoverableErrors[i]; - const componentStack = recoverableError.stack; - const digest = recoverableError.digest; - onRecoverableError(recoverableError.value, {componentStack, digest}); + const errorInfo = makeErrorInfo( + recoverableError.digest, + recoverableError.stack, + ); + onRecoverableError(recoverableError.value, errorInfo); } } @@ -2689,6 +2691,33 @@ function commitRootImpl( return null; } +function makeErrorInfo(digest: ?string, componentStack: ?string) { + if (__DEV__) { + const errorInfo = { + componentStack, + digest, + }; + Object.defineProperty(errorInfo, 'digest', { + configurable: false, + enumerable: true, + get() { + console.error( + 'You are accessing "digest" from the errorInfo object passed to onRecoverableError.' + + ' This property is deprecated and will be removed in a future version of React.' + + ' To access the digest of an Error look for this property on the Error instance itself.', + ); + return digest; + }, + }); + return errorInfo; + } else { + return { + digest, + componentStack, + }; + } +} + function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) { if (enableCache) { const pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);