@samitouri / QOS-React-1 / commits / a419575077

fix[devtools/useMemoCache]: add stub for useMemoCache in ReactDebugHook (#27472)

Currently, we have this error in our logs of the internal version of React DevTools: ``` TypeError: Cannot read properties of undefined (reading 'memoCache') at Proxy.useMemoCache (chrome-extension://dnjnjgbfilfphmojnmhliehogmojhclc/build/react_devtools_backend_compact.js:151:71) ``` Looking at the build files of the extension, it fails here: https://github.com/facebook/react/blob/dddfe688206dafa5646550d351eb9a8e9c53654a/packages/react-debug-tools/src/ReactDebugHooks.js#L333-L337 Looks like `updateQueue` can be `undefined`, as it is not defined in hook object here: https://github.com/facebook/react/blob/dddfe688206dafa5646550d351eb9a8e9c53654a/packages/react-reconciler/src/ReactFiberHooks.js#L180-L186 ~~Also, it looks like `useMemoCache` implementation doesn't expect this, so it should also result into TypeError here, line 1114:~~ https://github.com/facebook/react/blob/dddfe688206dafa5646550d351eb9a8e9c53654a/packages/react-reconciler/src/ReactFiberHooks.js#L1108-L1115 ~~Should this also be updated?~~

Ruslan Lesiutin committed Oct 17, 2023 at 18:39 UTC a4195750779dbd9a13e1615fbbd493bf2c5768ca
4 files changed +17 -47
packages/react-debug-tools/src/ReactDebugHooks.js
+6 -41
@@ -48,19 +48,9 @@ type Dispatch<A> = A => void;
48
49 let primitiveStackCache: null | Map<string, Array<any>> = null;
50
51 -type MemoCache = {
52 - data: Array<Array<any>>,
53 - index: number,
54 -};
55 -
56 -type FunctionComponentUpdateQueue = {
57 - memoCache?: MemoCache | null,
58 -};
59 -
51 type Hook = {
52 memoizedState: any,
53 next: Hook | null,
63 - updateQueue: FunctionComponentUpdateQueue | null,
54 };
55
56 function getPrimitiveStackCache(): Map<string, Array<any>> {
@@ -327,36 +317,11 @@ function useId(): string {
317 return id;
318 }
319
320 +// useMemoCache is an implementation detail of Forget's memoization
321 +// it should not be called directly in user-generated code
322 +// we keep it as a stub for dispatcher
323 function useMemoCache(size: number): Array<any> {
331 - const hook = nextHook();
332 - let memoCache: MemoCache;
333 - if (
334 - hook !== null &&
335 - hook.updateQueue !== null &&
336 - hook.updateQueue.memoCache != null
337 - ) {
338 - memoCache = hook.updateQueue.memoCache;
339 - } else {
340 - memoCache = {
341 - data: [],
342 - index: 0,
343 - };
344 - }
345 -
346 - let data = memoCache.data[memoCache.index];
347 - if (data === undefined) {
348 - const MEMO_CACHE_SENTINEL = Symbol.for('react.memo_cache_sentinel');
349 - data = new Array(size);
350 - for (let i = 0; i < size; i++) {
351 - data[i] = MEMO_CACHE_SENTINEL;
352 - }
353 - }
354 - hookLog.push({
355 - primitive: 'MemoCache',
356 - stackError: new Error(),
357 - value: data,
358 - });
359 - return data;
324 + return [];
325 }
326
327 const Dispatcher: DispatcherType = {
@@ -725,7 +690,7 @@ export function inspectHooks<Props>(
690 renderFunction: Props => React$Node,
691 props: Props,
692 currentDispatcher: ?CurrentDispatcherRef,
728 - includeHooksSource?: boolean = false,
693 + includeHooksSource: boolean = false,
694 ): HooksTree {
695 // DevTools will pass the current renderer's injected dispatcher.
696 // Other apps might compile debug hooks as part of their app though.
@@ -816,7 +781,7 @@ function resolveDefaultProps(Component: any, baseProps: any) {
781 export function inspectHooksOfFiber(
782 fiber: Fiber,
783 currentDispatcher: ?CurrentDispatcherRef,
819 - includeHooksSource?: boolean = false,
784 + includeHooksSource: boolean = false,
785 ): HooksTree {
786 // DevTools will pass the current renderer's injected dispatcher.
787 // Other apps might compile debug hooks as part of their app though.
packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js
+2 -6
@@ -634,7 +634,7 @@ describe('ReactHooksInspectionIntegration', () => {
634 });
635
636 // @gate enableUseMemoCacheHook
637 - it('should support useMemoCache hook', () => {
637 + it('useMemoCache should not be inspectable', () => {
638 function Foo() {
639 const $ = useMemoCache(1);
640 let t0;
@@ -653,11 +653,7 @@ describe('ReactHooksInspectionIntegration', () => {
653 const childFiber = renderer.root.findByType(Foo)._currentFiber();
654 const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
655
656 - expect(tree.length).toEqual(1);
657 - expect(tree[0].isStateEditable).toBe(false);
658 - expect(tree[0].name).toBe('MemoCache');
659 - expect(tree[0].value).toHaveLength(1);
660 - expect(tree[0].value[0]).toEqual(<div>{1}</div>);
656 + expect(tree.length).toEqual(0);
657 });
658
659 describe('useDebugValue', () => {
packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js
+2
@@ -18,6 +18,7 @@ import EdgeCaseObjects from './EdgeCaseObjects.js';
18 import NestedProps from './NestedProps';
19 import SimpleValues from './SimpleValues';
20 import SymbolKeys from './SymbolKeys';
21 +import UseMemoCache from './UseMemoCache';
22
23 // TODO Add Immutable JS example
24
@@ -34,6 +35,7 @@ export default function InspectableElements(): React.Node {
35 <EdgeCaseObjects />
36 <CircularReferences />
37 <SymbolKeys />
38 + <UseMemoCache />
39 </Fragment>
40 );
41 }
packages/react-devtools-shell/src/app/InspectableElements/UseMemoCache.js new
+7
@@ -0,0 +1,7 @@
1 +import * as React from 'react';
2 +
3 +export default function UseMemoCache(): React.Node {
4 + React.unstable_useMemoCache(1);
5 +
6 + return null;
7 +}