@samitouri / QOS-React-2 / commits / 112d0498c8

[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

Josh Story committed Sep 22, 2022 at 12:40 UTC 112d0498c8e05100f0a1fa0c4c0cc11534ae5353
5 files changed +108 -7
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+42 -1
@@ -92,7 +92,7 @@ describe('ReactDOMFizzServer', () => {
92 function expectErrors(errorsArr, toBeDevArr, toBeProdArr) {
93 const mappedErrows = errorsArr.map(({error, errorInfo}) => {
94 const stack = errorInfo && errorInfo.componentStack;
95 - const digest = errorInfo && errorInfo.digest;
95 + const digest = error.digest;
96 if (stack) {
97 return [error.message, digest, normalizeCodeLocInfo(stack)];
98 } else if (digest) {
@@ -3230,6 +3230,47 @@ describe('ReactDOMFizzServer', () => {
3230 );
3231 });
3232
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 +
3274 describe('error escaping', () => {
3275 it('escapes error hash, message, and component stack values in directly flushed errors (html escaping)', async () => {
3276 window.__outlet = {};
packages/react-reconciler/src/ReactFiberBeginWork.new.js
+1
@@ -2746,6 +2746,7 @@ function updateDehydratedSuspenseComponent(
2746 'client rendering.',
2747 );
2748 }
2749 + (error: any).digest = digest;
2750 const capturedValue = createCapturedValue(error, digest, stack);
2751 return retrySuspenseComponentWithoutHydrating(
2752 current,
packages/react-reconciler/src/ReactFiberBeginWork.old.js
+1
@@ -2746,6 +2746,7 @@ function updateDehydratedSuspenseComponent(
2746 'client rendering.',
2747 );
2748 }
2749 + (error: any).digest = digest;
2750 const capturedValue = createCapturedValue(error, digest, stack);
2751 return retrySuspenseComponentWithoutHydrating(
2752 current,
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+32 -3
@@ -2596,9 +2596,11 @@ function commitRootImpl(
2596 const onRecoverableError = root.onRecoverableError;
2597 for (let i = 0; i < recoverableErrors.length; i++) {
2598 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);
2604 }
2605 }
2606
@@ -2689,6 +2691,33 @@ function commitRootImpl(
2691 return null;
2692 }
2693
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 +
2721 function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) {
2722 if (enableCache) {
2723 const pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+32 -3
@@ -2596,9 +2596,11 @@ function commitRootImpl(
2596 const onRecoverableError = root.onRecoverableError;
2597 for (let i = 0; i < recoverableErrors.length; i++) {
2598 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);
2604 }
2605 }
2606
@@ -2689,6 +2691,33 @@ function commitRootImpl(
2691 return null;
2692 }
2693
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 +
2721 function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) {
2722 if (enableCache) {
2723 const pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);