Skip to content

Commit 112d049

Browse files
authored
[Fizz] Move digest from errorInfo to Error instance (#25313)
* suspense boundary error digest to Error instance and deprecate digest from errorInfo for onRecoverableError * fix closure escape
1 parent d1bb1c5 commit 112d049

File tree

5 files changed

+108
-7
lines changed

5 files changed

+108
-7
lines changed

packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js

+42-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ describe('ReactDOMFizzServer', () => {
9292
function expectErrors(errorsArr, toBeDevArr, toBeProdArr) {
9393
const mappedErrows = errorsArr.map(({error, errorInfo}) => {
9494
const stack = errorInfo && errorInfo.componentStack;
95-
const digest = errorInfo && errorInfo.digest;
95+
const digest = error.digest;
9696
if (stack) {
9797
return [error.message, digest, normalizeCodeLocInfo(stack)];
9898
} else if (digest) {
@@ -3230,6 +3230,47 @@ describe('ReactDOMFizzServer', () => {
32303230
);
32313231
});
32323232

3233+
it('warns in dev if you access digest from errorInfo in onRecoverableError', async () => {
3234+
await act(async () => {
3235+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
3236+
<div>
3237+
<Suspense fallback={'loading...'}>
3238+
<AsyncText text={'hello'} />
3239+
</Suspense>
3240+
</div>,
3241+
{
3242+
onError(error) {
3243+
return 'a digest';
3244+
},
3245+
},
3246+
);
3247+
rejectText('hello');
3248+
pipe(writable);
3249+
});
3250+
expect(getVisibleChildren(container)).toEqual(<div>loading...</div>);
3251+
3252+
ReactDOMClient.hydrateRoot(
3253+
container,
3254+
<div>
3255+
<Suspense fallback={'loading...'}>hello</Suspense>
3256+
</div>,
3257+
{
3258+
onRecoverableError(error, errorInfo) {
3259+
expect(() => {
3260+
expect(error.digest).toBe('a digest');
3261+
expect(errorInfo.digest).toBe('a digest');
3262+
}).toErrorDev(
3263+
'Warning: You are accessing "digest" from the errorInfo object passed to onRecoverableError.' +
3264+
' This property is deprecated and will be removed in a future version of React.' +
3265+
' To access the digest of an Error look for this property on the Error instance itself.',
3266+
{withoutStack: true},
3267+
);
3268+
},
3269+
},
3270+
);
3271+
expect(Scheduler).toFlushWithoutYielding();
3272+
});
3273+
32333274
describe('error escaping', () => {
32343275
it('escapes error hash, message, and component stack values in directly flushed errors (html escaping)', async () => {
32353276
window.__outlet = {};

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

+1
Original file line numberDiff line numberDiff line change
@@ -2746,6 +2746,7 @@ function updateDehydratedSuspenseComponent(
27462746
'client rendering.',
27472747
);
27482748
}
2749+
(error: any).digest = digest;
27492750
const capturedValue = createCapturedValue(error, digest, stack);
27502751
return retrySuspenseComponentWithoutHydrating(
27512752
current,

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

+1
Original file line numberDiff line numberDiff line change
@@ -2746,6 +2746,7 @@ function updateDehydratedSuspenseComponent(
27462746
'client rendering.',
27472747
);
27482748
}
2749+
(error: any).digest = digest;
27492750
const capturedValue = createCapturedValue(error, digest, stack);
27502751
return retrySuspenseComponentWithoutHydrating(
27512752
current,

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

+32-3
Original file line numberDiff line numberDiff line change
@@ -2596,9 +2596,11 @@ function commitRootImpl(
25962596
const onRecoverableError = root.onRecoverableError;
25972597
for (let i = 0; i < recoverableErrors.length; i++) {
25982598
const recoverableError = recoverableErrors[i];
2599-
const componentStack = recoverableError.stack;
2600-
const digest = recoverableError.digest;
2601-
onRecoverableError(recoverableError.value, {componentStack, digest});
2599+
const errorInfo = makeErrorInfo(
2600+
recoverableError.digest,
2601+
recoverableError.stack,
2602+
);
2603+
onRecoverableError(recoverableError.value, errorInfo);
26022604
}
26032605
}
26042606

@@ -2689,6 +2691,33 @@ function commitRootImpl(
26892691
return null;
26902692
}
26912693

2694+
function makeErrorInfo(digest: ?string, componentStack: ?string) {
2695+
if (__DEV__) {
2696+
const errorInfo = {
2697+
componentStack,
2698+
digest,
2699+
};
2700+
Object.defineProperty(errorInfo, 'digest', {
2701+
configurable: false,
2702+
enumerable: true,
2703+
get() {
2704+
console.error(
2705+
'You are accessing "digest" from the errorInfo object passed to onRecoverableError.' +
2706+
' This property is deprecated and will be removed in a future version of React.' +
2707+
' To access the digest of an Error look for this property on the Error instance itself.',
2708+
);
2709+
return digest;
2710+
},
2711+
});
2712+
return errorInfo;
2713+
} else {
2714+
return {
2715+
digest,
2716+
componentStack,
2717+
};
2718+
}
2719+
}
2720+
26922721
function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) {
26932722
if (enableCache) {
26942723
const pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);

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

+32-3
Original file line numberDiff line numberDiff line change
@@ -2596,9 +2596,11 @@ function commitRootImpl(
25962596
const onRecoverableError = root.onRecoverableError;
25972597
for (let i = 0; i < recoverableErrors.length; i++) {
25982598
const recoverableError = recoverableErrors[i];
2599-
const componentStack = recoverableError.stack;
2600-
const digest = recoverableError.digest;
2601-
onRecoverableError(recoverableError.value, {componentStack, digest});
2599+
const errorInfo = makeErrorInfo(
2600+
recoverableError.digest,
2601+
recoverableError.stack,
2602+
);
2603+
onRecoverableError(recoverableError.value, errorInfo);
26022604
}
26032605
}
26042606

@@ -2689,6 +2691,33 @@ function commitRootImpl(
26892691
return null;
26902692
}
26912693

2694+
function makeErrorInfo(digest: ?string, componentStack: ?string) {
2695+
if (__DEV__) {
2696+
const errorInfo = {
2697+
componentStack,
2698+
digest,
2699+
};
2700+
Object.defineProperty(errorInfo, 'digest', {
2701+
configurable: false,
2702+
enumerable: true,
2703+
get() {
2704+
console.error(
2705+
'You are accessing "digest" from the errorInfo object passed to onRecoverableError.' +
2706+
' This property is deprecated and will be removed in a future version of React.' +
2707+
' To access the digest of an Error look for this property on the Error instance itself.',
2708+
);
2709+
return digest;
2710+
},
2711+
});
2712+
return errorInfo;
2713+
} else {
2714+
return {
2715+
digest,
2716+
componentStack,
2717+
};
2718+
}
2719+
}
2720+
26922721
function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) {
26932722
if (enableCache) {
26942723
const pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);

0 commit comments

Comments
 (0)