Skip to content

Commit

Permalink
[DevTools] Add support for useMemoCache
Browse files Browse the repository at this point in the history
useMemoCache wasn't previously supported in the DevTools, so any attempt
to inspect a component using the hook would result in a
`dispatcher.useMemoCache is not a function (it is undefined)` error.
  • Loading branch information
poteto committed Apr 24, 2023
1 parent 2fa6323 commit bd36112
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 0 deletions.
48 changes: 48 additions & 0 deletions packages/react-debug-tools/src/ReactDebugHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
ForwardRef,
} from 'react-reconciler/src/ReactWorkTags';

const MEMO_CACHE_SENTINEL = Symbol.for('react.memo_cache_sentinel');

type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;

// Used to track hooks called during a render
Expand All @@ -51,9 +53,19 @@ type Dispatch<A> = A => void;

let primitiveStackCache: null | Map<string, Array<any>> = null;

type MemoCache = {
data: Array<Array<any>>,
index: number,
};

type FunctionComponentUpdateQueue = {
memoCache?: MemoCache | null,
};

type Hook = {
memoizedState: any,
next: Hook | null,
updateQueue: FunctionComponentUpdateQueue | null,
};

function getPrimitiveStackCache(): Map<string, Array<any>> {
Expand All @@ -79,6 +91,10 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
Dispatcher.useDebugValue(null);
Dispatcher.useCallback(() => {});
Dispatcher.useMemo(() => null);
if (typeof Dispatcher.useMemoCache === 'function') {
// This type check is for Flow only.
Dispatcher.useMemoCache(0);
}
} finally {
readHookLog = hookLog;
hookLog = [];
Expand Down Expand Up @@ -333,6 +349,37 @@ function useId(): string {
return id;
}

function useMemoCache(size: number): Array<any> {
const hook = nextHook();
let memoCache: MemoCache;
if (
hook !== null &&
hook.updateQueue !== null &&
hook.updateQueue.memoCache != null
) {
memoCache = hook.updateQueue.memoCache;
} else {
memoCache = {
data: [],
index: 0,
};
}

let data = memoCache.data[memoCache.index];
if (data === undefined) {
data = new Array(size);
for (let i = 0; i < size; i++) {
data[i] = MEMO_CACHE_SENTINEL;
}
}
hookLog.push({
primitive: 'MemoCache',
stackError: new Error(),
value: data,
});
return data;
}

const Dispatcher: DispatcherType = {
use,
readContext,
Expand All @@ -345,6 +392,7 @@ const Dispatcher: DispatcherType = {
useLayoutEffect,
useInsertionEffect,
useMemo,
useMemoCache,
useReducer,
useRef,
useState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,42 @@ describe('ReactHooksInspectionIntegration', () => {
});
});

// @gate enableUseMemoCacheHook
it('should support useMemoCache hook', () => {
function Foo() {
const $ = React.unstable_useMemoCache(1);
let t0;

if ($[0] === Symbol.for('react.memo_cache_sentinel')) {
t0 = <div>{1}</div>;
$[0] = t0;
} else {
t0 = $[0];
}

return t0;
}

const renderer = ReactTestRenderer.create(<Foo />);
const childFiber = renderer.root.findByType(Foo)._currentFiber();
const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);

expect(tree.length).toEqual(1);
expect(tree[0]).toMatchInlineSnapshot(`
{
"id": 0,
"isStateEditable": false,
"name": "MemoCache",
"subHooks": [],
"value": [
<div>
1
</div>,
],
}
`);
});

describe('useDebugValue', () => {
it('should support inspectable values for multiple custom hooks', () => {
function useLabeledValue(label) {
Expand Down

0 comments on commit bd36112

Please sign in to comment.