[DevTools] Add support for useMemoCache (#26696)

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.
This commit is contained in:
lauren 2023-04-25 09:19:25 -07:00 committed by GitHub
parent ed545ae3d3
commit 25b99efe0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 1 deletions

View File

@ -51,9 +51,19 @@ type Dispatch<A> = A => void;
let primitiveStackCache: null | Map<string, Array<any>> = null; let primitiveStackCache: null | Map<string, Array<any>> = null;
type MemoCache = {
data: Array<Array<any>>,
index: number,
};
type FunctionComponentUpdateQueue = {
memoCache?: MemoCache | null,
};
type Hook = { type Hook = {
memoizedState: any, memoizedState: any,
next: Hook | null, next: Hook | null,
updateQueue: FunctionComponentUpdateQueue | null,
}; };
function getPrimitiveStackCache(): Map<string, Array<any>> { function getPrimitiveStackCache(): Map<string, Array<any>> {
@ -79,6 +89,10 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
Dispatcher.useDebugValue(null); Dispatcher.useDebugValue(null);
Dispatcher.useCallback(() => {}); Dispatcher.useCallback(() => {});
Dispatcher.useMemo(() => null); Dispatcher.useMemo(() => null);
if (typeof Dispatcher.useMemoCache === 'function') {
// This type check is for Flow only.
Dispatcher.useMemoCache(0);
}
} finally { } finally {
readHookLog = hookLog; readHookLog = hookLog;
hookLog = []; hookLog = [];
@ -333,6 +347,38 @@ function useId(): string {
return id; 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) {
const MEMO_CACHE_SENTINEL = Symbol.for('react.memo_cache_sentinel');
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 = { const Dispatcher: DispatcherType = {
use, use,
readContext, readContext,
@ -345,6 +391,7 @@ const Dispatcher: DispatcherType = {
useLayoutEffect, useLayoutEffect,
useInsertionEffect, useInsertionEffect,
useMemo, useMemo,
useMemoCache,
useReducer, useReducer,
useRef, useRef,
useState, useState,

View File

@ -14,6 +14,7 @@ let React;
let ReactTestRenderer; let ReactTestRenderer;
let ReactDebugTools; let ReactDebugTools;
let act; let act;
let useMemoCache;
describe('ReactHooksInspectionIntegration', () => { describe('ReactHooksInspectionIntegration', () => {
beforeEach(() => { beforeEach(() => {
@ -22,6 +23,7 @@ describe('ReactHooksInspectionIntegration', () => {
ReactTestRenderer = require('react-test-renderer'); ReactTestRenderer = require('react-test-renderer');
act = require('internal-test-utils').act; act = require('internal-test-utils').act;
ReactDebugTools = require('react-debug-tools'); ReactDebugTools = require('react-debug-tools');
useMemoCache = React.unstable_useMemoCache;
}); });
it('should inspect the current state of useState hooks', async () => { it('should inspect the current state of useState hooks', async () => {
@ -633,6 +635,33 @@ describe('ReactHooksInspectionIntegration', () => {
}); });
}); });
// @gate enableUseMemoCacheHook
it('should support useMemoCache hook', () => {
function Foo() {
const $ = 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].isStateEditable).toBe(false);
expect(tree[0].name).toBe('MemoCache');
expect(tree[0].value).toHaveLength(1);
expect(tree[0].value[0]).toEqual(<div>{1}</div>);
});
describe('useDebugValue', () => { describe('useDebugValue', () => {
it('should support inspectable values for multiple custom hooks', () => { it('should support inspectable values for multiple custom hooks', () => {
function useLabeledValue(label) { function useLabeledValue(label) {

View File

@ -39,7 +39,7 @@ export const disableModulePatternComponents = false;
export const enableSuspenseAvoidThisFallback = false; export const enableSuspenseAvoidThisFallback = false;
export const enableSuspenseAvoidThisFallbackFizz = false; export const enableSuspenseAvoidThisFallbackFizz = false;
export const enableCPUSuspense = false; export const enableCPUSuspense = false;
export const enableUseMemoCacheHook = false; export const enableUseMemoCacheHook = true;
export const enableUseEffectEventHook = false; export const enableUseEffectEventHook = false;
export const enableClientRenderFallbackOnTextMismatch = true; export const enableClientRenderFallbackOnTextMismatch = true;
export const enableComponentStackLocations = true; export const enableComponentStackLocations = true;