From 510bc28ee0b7d4488c58f5bbad14a3d072d0740e Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Fri, 9 Feb 2024 22:14:58 -0500 Subject: [PATCH] Introspect use() Hooks with Debug Tools --- .../react-debug-tools/src/ReactDebugHooks.js | 131 ++++++++++++++++-- .../__tests__/ReactHooksInspection-test.js | 102 ++++++++++++++ .../ReactHooksInspectionIntegration-test.js | 119 +++++++++++++++- .../src/__tests__/inspectedElement-test.js | 11 ++ 4 files changed, 347 insertions(+), 16 deletions(-) diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index e8dc2578be11a..962d7d8bba2b5 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -13,6 +13,8 @@ import type { ReactProviderType, StartTransitionOptions, Usable, + Thenable, + ReactDebugInfo, } from 'shared/ReactTypes'; import type { Fiber, @@ -41,6 +43,7 @@ type HookLogEntry = { primitive: string, stackError: Error, value: mixed, + debugInfo: ReactDebugInfo | null, }; let hookLog: Array = []; @@ -93,6 +96,27 @@ function getPrimitiveStackCache(): Map> { // This type check is for Flow only. Dispatcher.useFormState((s: mixed, p: mixed) => s, null); } + if (typeof Dispatcher.use === 'function') { + // This type check is for Flow only. + Dispatcher.use( + ({ + $$typeof: REACT_CONTEXT_TYPE, + _currentValue: null, + }: any), + ); + Dispatcher.use({ + then() {}, + status: 'fulfilled', + value: null, + }); + try { + Dispatcher.use( + ({ + then() {}, + }: any), + ); + } catch (x) {} + } } finally { readHookLog = hookLog; hookLog = []; @@ -122,22 +146,57 @@ function readContext(context: ReactContext): T { return context._currentValue; } +const SuspenseException: mixed = new Error( + "Suspense Exception: This is not a real error! It's an implementation " + + 'detail of `use` to interrupt the current render. You must either ' + + 'rethrow it immediately, or move the `use` call outside of the ' + + '`try/catch` block. Capturing without rethrowing will lead to ' + + 'unexpected behavior.\n\n' + + 'To handle async errors, wrap your component in an error boundary, or ' + + "call the promise's `.catch` method and pass the result to `use`", +); + function use(usable: Usable): T { if (usable !== null && typeof usable === 'object') { // $FlowFixMe[method-unbinding] if (typeof usable.then === 'function') { - // TODO: What should this do if it receives an unresolved promise? - throw new Error( - 'Support for `use(Promise)` not yet implemented in react-debug-tools.', - ); + const thenable: Thenable = (usable: any); + switch (thenable.status) { + case 'fulfilled': { + const fulfilledValue: T = thenable.value; + hookLog.push({ + primitive: 'Promise', + stackError: new Error(), + value: fulfilledValue, + debugInfo: + thenable._debugInfo === undefined ? null : thenable._debugInfo, + }); + return fulfilledValue; + } + case 'rejected': { + const rejectedError = thenable.reason; + throw rejectedError; + } + } + // If this was an uncached Promise we have to abandon this attempt + // but we can still emit anything up until this point. + hookLog.push({ + primitive: 'Unresolved', + stackError: new Error(), + value: thenable, + debugInfo: + thenable._debugInfo === undefined ? null : thenable._debugInfo, + }); + throw SuspenseException; } else if (usable.$$typeof === REACT_CONTEXT_TYPE) { const context: ReactContext = (usable: any); const value = readContext(context); hookLog.push({ - primitive: 'Use', + primitive: 'Context (use)', stackError: new Error(), value, + debugInfo: null, }); return value; @@ -153,6 +212,7 @@ function useContext(context: ReactContext): T { primitive: 'Context', stackError: new Error(), value: context._currentValue, + debugInfo: null, }); return context._currentValue; } @@ -168,7 +228,12 @@ function useState( ? // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types initialState() : initialState; - hookLog.push({primitive: 'State', stackError: new Error(), value: state}); + hookLog.push({ + primitive: 'State', + stackError: new Error(), + value: state, + debugInfo: null, + }); return [state, (action: BasicStateAction) => {}]; } @@ -188,6 +253,7 @@ function useReducer( primitive: 'Reducer', stackError: new Error(), value: state, + debugInfo: null, }); return [state, (action: A) => {}]; } @@ -199,6 +265,7 @@ function useRef(initialValue: T): {current: T} { primitive: 'Ref', stackError: new Error(), value: ref.current, + debugInfo: null, }); return ref; } @@ -209,6 +276,7 @@ function useCacheRefresh(): () => void { primitive: 'CacheRefresh', stackError: new Error(), value: hook !== null ? hook.memoizedState : function refresh() {}, + debugInfo: null, }); return () => {}; } @@ -222,6 +290,7 @@ function useLayoutEffect( primitive: 'LayoutEffect', stackError: new Error(), value: create, + debugInfo: null, }); } @@ -234,6 +303,7 @@ function useInsertionEffect( primitive: 'InsertionEffect', stackError: new Error(), value: create, + debugInfo: null, }); } @@ -242,7 +312,12 @@ function useEffect( inputs: Array | void | null, ): void { nextHook(); - hookLog.push({primitive: 'Effect', stackError: new Error(), value: create}); + hookLog.push({ + primitive: 'Effect', + stackError: new Error(), + value: create, + debugInfo: null, + }); } function useImperativeHandle( @@ -263,6 +338,7 @@ function useImperativeHandle( primitive: 'ImperativeHandle', stackError: new Error(), value: instance, + debugInfo: null, }); } @@ -271,6 +347,7 @@ function useDebugValue(value: any, formatterFn: ?(value: any) => any) { primitive: 'DebugValue', stackError: new Error(), value: typeof formatterFn === 'function' ? formatterFn(value) : value, + debugInfo: null, }); } @@ -280,6 +357,7 @@ function useCallback(callback: T, inputs: Array | void | null): T { primitive: 'Callback', stackError: new Error(), value: hook !== null ? hook.memoizedState[0] : callback, + debugInfo: null, }); return callback; } @@ -290,7 +368,12 @@ function useMemo( ): T { const hook = nextHook(); const value = hook !== null ? hook.memoizedState[0] : nextCreate(); - hookLog.push({primitive: 'Memo', stackError: new Error(), value}); + hookLog.push({ + primitive: 'Memo', + stackError: new Error(), + value, + debugInfo: null, + }); return value; } @@ -309,6 +392,7 @@ function useSyncExternalStore( primitive: 'SyncExternalStore', stackError: new Error(), value, + debugInfo: null, }); return value; } @@ -326,6 +410,7 @@ function useTransition(): [ primitive: 'Transition', stackError: new Error(), value: undefined, + debugInfo: null, }); return [false, callback => {}]; } @@ -336,6 +421,7 @@ function useDeferredValue(value: T, initialValue?: T): T { primitive: 'DeferredValue', stackError: new Error(), value: hook !== null ? hook.memoizedState : value, + debugInfo: null, }); return value; } @@ -347,6 +433,7 @@ function useId(): string { primitive: 'Id', stackError: new Error(), value: id, + debugInfo: null, }); return id; } @@ -395,6 +482,7 @@ function useOptimistic( primitive: 'Optimistic', stackError: new Error(), value: state, + debugInfo: null, }); return [state, (action: A) => {}]; } @@ -416,6 +504,7 @@ function useFormState( primitive: 'FormState', stackError: new Error(), value: state, + debugInfo: null, }); return [state, (payload: P) => {}]; } @@ -480,6 +569,7 @@ export type HooksNode = { name: string, value: mixed, subHooks: Array, + debugInfo: null | ReactDebugInfo, hookSource?: HookSource, }; export type HooksTree = Array; @@ -546,6 +636,15 @@ function isReactWrapper(functionName: any, primitiveName: string) { if (!functionName) { return false; } + switch (primitiveName) { + case 'Context': + case 'Context (use)': + case 'Promise': + case 'Unresolved': + if (functionName.endsWith('use')) { + return true; + } + } const expectedPrimitiveName = 'use' + primitiveName; if (functionName.length < expectedPrimitiveName.length) { return false; @@ -661,6 +760,7 @@ function buildTree( name: parseCustomHookName(stack[j - 1].functionName), value: undefined, subHooks: children, + debugInfo: null, }; if (includeHooksSource) { @@ -678,25 +778,29 @@ function buildTree( } prevStack = stack; } - const {primitive} = hook; + const {primitive, debugInfo} = hook; // For now, the "id" of stateful hooks is just the stateful hook index. // Custom hooks have no ids, nor do non-stateful native hooks (e.g. Context, DebugValue). const id = primitive === 'Context' || + primitive === 'Context (use)' || primitive === 'DebugValue' || - primitive === 'Use' + primitive === 'Promise' || + primitive === 'Unresolved' ? null : nativeHookID++; // For the time being, only State and Reducer hooks support runtime overrides. const isStateEditable = primitive === 'Reducer' || primitive === 'State'; + const name = primitive === 'Context (use)' ? 'Context' : primitive; const levelChild: HooksNode = { id, isStateEditable, - name: primitive, + name: name, value: hook.value, subHooks: [], + debugInfo: debugInfo, }; if (includeHooksSource) { @@ -762,6 +866,11 @@ function processDebugValues( function handleRenderFunctionError(error: any): void { // original error might be any type. + if (error === SuspenseException) { + // An uncached Promise was used. We can't synchronously resolve the rest of + // the Hooks but we can at least show what ever we got so far. + return; + } if ( error instanceof Error && error.name === 'ReactDebugToolsUnsupportedHookError' diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js index 48037fc0321d6..4846a1fb20750 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js @@ -31,6 +31,7 @@ describe('ReactHooksInspection', () => { id: 0, name: 'State', value: 'hello world', + debugInfo: null, subHooks: [], }, ]); @@ -53,12 +54,14 @@ describe('ReactHooksInspection', () => { id: null, name: 'Custom', value: __DEV__ ? 'custom hook label' : undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 0, name: 'State', value: 'hello world', + debugInfo: null, subHooks: [], }, ], @@ -89,11 +92,13 @@ describe('ReactHooksInspection', () => { id: null, name: 'Custom', value: undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 0, name: 'State', + debugInfo: null, subHooks: [], value: 'hello', }, @@ -101,6 +106,7 @@ describe('ReactHooksInspection', () => { isStateEditable: false, id: 1, name: 'Effect', + debugInfo: null, subHooks: [], value: effect, }, @@ -111,12 +117,14 @@ describe('ReactHooksInspection', () => { id: null, name: 'Custom', value: undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 2, name: 'State', value: 'world', + debugInfo: null, subHooks: [], }, { @@ -124,6 +132,7 @@ describe('ReactHooksInspection', () => { id: 3, name: 'Effect', value: effect, + debugInfo: null, subHooks: [], }, ], @@ -164,18 +173,21 @@ describe('ReactHooksInspection', () => { id: null, name: 'Bar', value: undefined, + debugInfo: null, subHooks: [ { isStateEditable: false, id: null, name: 'Custom', value: undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 0, name: 'Reducer', value: 'hello', + debugInfo: null, subHooks: [], }, { @@ -183,6 +195,7 @@ describe('ReactHooksInspection', () => { id: 1, name: 'Effect', value: effect, + debugInfo: null, subHooks: [], }, ], @@ -192,6 +205,7 @@ describe('ReactHooksInspection', () => { id: 2, name: 'LayoutEffect', value: effect, + debugInfo: null, subHooks: [], }, ], @@ -201,23 +215,27 @@ describe('ReactHooksInspection', () => { id: null, name: 'Baz', value: undefined, + debugInfo: null, subHooks: [ { isStateEditable: false, id: 3, name: 'LayoutEffect', value: effect, + debugInfo: null, subHooks: [], }, { isStateEditable: false, id: null, name: 'Custom', + debugInfo: null, subHooks: [ { isStateEditable: true, id: 4, name: 'Reducer', + debugInfo: null, subHooks: [], value: 'world', }, @@ -225,6 +243,7 @@ describe('ReactHooksInspection', () => { isStateEditable: false, id: 5, name: 'Effect', + debugInfo: null, subHooks: [], value: effect, }, @@ -249,6 +268,7 @@ describe('ReactHooksInspection', () => { id: null, name: 'Context', value: 'default', + debugInfo: null, subHooks: [], }, ]); @@ -287,6 +307,86 @@ describe('ReactHooksInspection', () => { expect(setterCalls[1]).toBe(initial); }); + it('should inspect use() calls for Promise and Context', async () => { + const MyContext = React.createContext('hi'); + const promise = Promise.resolve('world'); + await promise; + promise.status = 'fulfilled'; + promise.value = 'world'; + promise._debugInfo = [{name: 'Hello'}]; + + function useCustom() { + const value = React.use(promise); + const [state] = React.useState(value); + return state; + } + function Foo(props) { + const value1 = React.use(MyContext); + const value2 = useCustom(); + return ( +
+ {value1} {value2} +
+ ); + } + const tree = ReactDebugTools.inspectHooks(Foo, {}); + expect(tree).toEqual([ + { + isStateEditable: false, + id: null, + name: 'Context', + value: 'hi', + debugInfo: null, + subHooks: [], + }, + { + isStateEditable: false, + id: null, + name: 'Custom', + value: undefined, + debugInfo: null, + subHooks: [ + { + isStateEditable: false, + id: null, + name: 'Promise', + value: 'world', + debugInfo: [{name: 'Hello'}], + subHooks: [], + }, + { + isStateEditable: true, + id: 0, + name: 'State', + value: 'world', + debugInfo: null, + subHooks: [], + }, + ], + }, + ]); + }); + + it('should inspect use() calls for unresolved Promise', () => { + const promise = Promise.resolve('hi'); + + function Foo(props) { + const value = React.use(promise); + return
{value}
; + } + const tree = ReactDebugTools.inspectHooks(Foo, {}); + expect(tree).toEqual([ + { + isStateEditable: false, + id: null, + name: 'Unresolved', + value: promise, + debugInfo: null, + subHooks: [], + }, + ]); + }); + describe('useDebugValue', () => { it('should be ignored when called outside of a custom hook', () => { function Foo(props) { @@ -313,11 +413,13 @@ describe('ReactHooksInspection', () => { id: null, name: 'Custom', value: __DEV__ ? 'bar:123' : undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 0, name: 'State', + debugInfo: null, subHooks: [], value: 0, }, diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js index fc9d338b3f377..b6224f2dfa76e 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js @@ -48,6 +48,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 0, name: 'State', value: 'hello', + debugInfo: null, subHooks: [], }, { @@ -55,6 +56,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 1, name: 'State', value: 'world', + debugInfo: null, subHooks: [], }, ]); @@ -73,6 +75,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 0, name: 'State', value: 'Hi', + debugInfo: null, subHooks: [], }, { @@ -80,6 +83,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 1, name: 'State', value: 'world', + debugInfo: null, subHooks: [], }, ]); @@ -95,6 +99,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 0, name: 'State', value: 'Hi', + debugInfo: null, subHooks: [], }, { @@ -102,6 +107,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 1, name: 'State', value: 'world!', + debugInfo: null, subHooks: [], }, ]); @@ -157,6 +163,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 0, name: 'State', value: 'a', + debugInfo: null, subHooks: [], }, { @@ -164,14 +171,23 @@ describe('ReactHooksInspectionIntegration', () => { id: 1, name: 'Reducer', value: 'b', + debugInfo: null, + subHooks: [], + }, + { + isStateEditable: false, + id: 2, + name: 'Ref', + value: 'c', + debugInfo: null, subHooks: [], }, - {isStateEditable: false, id: 2, name: 'Ref', value: 'c', subHooks: []}, { isStateEditable: false, id: 3, name: 'LayoutEffect', value: effect, + debugInfo: null, subHooks: [], }, { @@ -179,6 +195,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 4, name: 'Effect', value: effect, + debugInfo: null, subHooks: [], }, { @@ -186,6 +203,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 5, name: 'ImperativeHandle', value: outsideRef.current, + debugInfo: null, subHooks: [], }, { @@ -193,6 +211,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 6, name: 'Memo', value: 'ab', + debugInfo: null, subHooks: [], }, { @@ -200,6 +219,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 7, name: 'Callback', value: updateStates, + debugInfo: null, subHooks: [], }, ]); @@ -217,6 +237,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 0, name: 'State', value: 'A', + debugInfo: null, subHooks: [], }, { @@ -224,14 +245,23 @@ describe('ReactHooksInspectionIntegration', () => { id: 1, name: 'Reducer', value: 'B', + debugInfo: null, + subHooks: [], + }, + { + isStateEditable: false, + id: 2, + name: 'Ref', + value: 'C', + debugInfo: null, subHooks: [], }, - {isStateEditable: false, id: 2, name: 'Ref', value: 'C', subHooks: []}, { isStateEditable: false, id: 3, name: 'LayoutEffect', value: effect, + debugInfo: null, subHooks: [], }, { @@ -239,6 +269,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 4, name: 'Effect', value: effect, + debugInfo: null, subHooks: [], }, { @@ -246,6 +277,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 5, name: 'ImperativeHandle', value: outsideRef.current, + debugInfo: null, subHooks: [], }, { @@ -253,6 +285,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 6, name: 'Memo', value: 'Ab', + debugInfo: null, subHooks: [], }, { @@ -260,6 +293,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 7, name: 'Callback', value: updateStates, + debugInfo: null, subHooks: [], }, ]); @@ -317,6 +351,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 0, name: 'State', value: 'a', + debugInfo: null, subHooks: [], }, { @@ -324,14 +359,23 @@ describe('ReactHooksInspectionIntegration', () => { id: 1, name: 'Reducer', value: 'b', + debugInfo: null, + subHooks: [], + }, + { + isStateEditable: false, + id: 2, + name: 'Ref', + value: 'c', + debugInfo: null, subHooks: [], }, - {isStateEditable: false, id: 2, name: 'Ref', value: 'c', subHooks: []}, { isStateEditable: false, id: 3, name: 'InsertionEffect', value: effect, + debugInfo: null, subHooks: [], }, { @@ -339,6 +383,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 4, name: 'LayoutEffect', value: effect, + debugInfo: null, subHooks: [], }, { @@ -346,6 +391,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 5, name: 'Effect', value: effect, + debugInfo: null, subHooks: [], }, { @@ -353,6 +399,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 6, name: 'ImperativeHandle', value: outsideRef.current, + debugInfo: null, subHooks: [], }, { @@ -360,6 +407,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 7, name: 'Memo', value: 'ab', + debugInfo: null, subHooks: [], }, { @@ -367,6 +415,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 8, name: 'Callback', value: updateStates, + debugInfo: null, subHooks: [], }, ]); @@ -384,6 +433,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 0, name: 'State', value: 'A', + debugInfo: null, subHooks: [], }, { @@ -391,14 +441,23 @@ describe('ReactHooksInspectionIntegration', () => { id: 1, name: 'Reducer', value: 'B', + debugInfo: null, + subHooks: [], + }, + { + isStateEditable: false, + id: 2, + name: 'Ref', + value: 'C', + debugInfo: null, subHooks: [], }, - {isStateEditable: false, id: 2, name: 'Ref', value: 'C', subHooks: []}, { isStateEditable: false, id: 3, name: 'InsertionEffect', value: effect, + debugInfo: null, subHooks: [], }, { @@ -406,6 +465,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 4, name: 'LayoutEffect', value: effect, + debugInfo: null, subHooks: [], }, { @@ -413,6 +473,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 5, name: 'Effect', value: effect, + debugInfo: null, subHooks: [], }, { @@ -420,6 +481,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 6, name: 'ImperativeHandle', value: outsideRef.current, + debugInfo: null, subHooks: [], }, { @@ -427,6 +489,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 7, name: 'Memo', value: 'Ab', + debugInfo: null, subHooks: [], }, { @@ -434,6 +497,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 8, name: 'Callback', value: updateStates, + debugInfo: null, subHooks: [], }, ]); @@ -458,6 +522,7 @@ describe('ReactHooksInspectionIntegration', () => { id: null, name: 'Context', value: 'contextual', + debugInfo: null, subHooks: [], }, ]); @@ -480,6 +545,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 0, name: 'ImperativeHandle', value: obj, + debugInfo: null, subHooks: [], }, ]); @@ -501,6 +567,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 0, name: 'State', value: 'hello', + debugInfo: null, subHooks: [], }, ]); @@ -524,12 +591,14 @@ describe('ReactHooksInspectionIntegration', () => { id: null, name: 'Custom', value: undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 0, name: 'State', value: 'hello', + debugInfo: null, subHooks: [], }, ], @@ -553,6 +622,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Transition', value: undefined, + debugInfo: null, subHooks: [], }, { @@ -560,6 +630,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 'hello', + debugInfo: null, subHooks: [], }, { @@ -567,6 +638,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 'not used', + debugInfo: null, subHooks: [], }, ]); @@ -588,6 +660,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'DeferredValue', value: 'abc', + debugInfo: null, subHooks: [], }, { @@ -595,6 +668,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 1, + debugInfo: null, subHooks: [], }, { @@ -602,6 +676,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 2, + debugInfo: null, subHooks: [], }, ]); @@ -630,6 +705,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: true, name: 'State', value: 'hello', + debugInfo: null, subHooks: [], }); }); @@ -721,12 +797,14 @@ describe('ReactHooksInspectionIntegration', () => { id: null, name: 'LabeledValue', value: __DEV__ ? 'custom label a' : undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 0, name: 'State', value: 'a', + debugInfo: null, subHooks: [], }, ], @@ -736,6 +814,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 1, name: 'State', value: 'b', + debugInfo: null, subHooks: [], }, { @@ -743,12 +822,14 @@ describe('ReactHooksInspectionIntegration', () => { id: null, name: 'Anonymous', value: undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 2, name: 'State', value: 'c', + debugInfo: null, subHooks: [], }, ], @@ -758,12 +839,14 @@ describe('ReactHooksInspectionIntegration', () => { id: null, name: 'LabeledValue', value: __DEV__ ? 'custom label d' : undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 3, name: 'State', value: 'd', + debugInfo: null, subHooks: [], }, ], @@ -793,18 +876,21 @@ describe('ReactHooksInspectionIntegration', () => { id: null, name: 'Outer', value: __DEV__ ? 'outer' : undefined, + debugInfo: null, subHooks: [ { isStateEditable: false, id: null, name: 'Inner', value: __DEV__ ? 'inner' : undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 0, name: 'State', value: 0, + debugInfo: null, subHooks: [], }, ], @@ -840,12 +926,14 @@ describe('ReactHooksInspectionIntegration', () => { id: null, name: 'SingleLabelCustom', value: __DEV__ ? 'single one' : undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 0, name: 'State', value: 0, + debugInfo: null, subHooks: [], }, ], @@ -855,12 +943,14 @@ describe('ReactHooksInspectionIntegration', () => { id: null, name: 'MultiLabelCustom', value: __DEV__ ? ['one', 'two', 'three'] : undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 1, name: 'State', value: 0, + debugInfo: null, subHooks: [], }, ], @@ -870,12 +960,14 @@ describe('ReactHooksInspectionIntegration', () => { id: null, name: 'SingleLabelCustom', value: __DEV__ ? 'single two' : undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 2, name: 'State', value: 0, + debugInfo: null, subHooks: [], }, ], @@ -912,11 +1004,13 @@ describe('ReactHooksInspectionIntegration', () => { id: null, name: 'Custom', value: __DEV__ ? 'bar:123' : undefined, + debugInfo: null, subHooks: [ { isStateEditable: true, id: 0, name: 'State', + debugInfo: null, subHooks: [], value: 0, }, @@ -963,6 +1057,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 0, name: 'State', value: 'def', + debugInfo: null, subHooks: [], }, ]); @@ -1056,6 +1151,7 @@ describe('ReactHooksInspectionIntegration', () => { id: null, name: 'Context', value: 1, + debugInfo: null, subHooks: [], }, { @@ -1063,6 +1159,7 @@ describe('ReactHooksInspectionIntegration', () => { id: 0, name: 'State', value: {count: 2}, + debugInfo: null, subHooks: [], }, ]); @@ -1089,6 +1186,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'SyncExternalStore', value: 'snapshot', + debugInfo: null, subHooks: [], }, { @@ -1096,6 +1194,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 'memo', + debugInfo: null, subHooks: [], }, { @@ -1103,6 +1202,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 'not used', + debugInfo: null, subHooks: [], }, ]); @@ -1125,8 +1225,9 @@ describe('ReactHooksInspectionIntegration', () => { { id: null, isStateEditable: false, - name: 'Use', + name: 'Context', value: 'default', + debugInfo: null, subHooks: [], }, { @@ -1134,6 +1235,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 'memo', + debugInfo: null, subHooks: [], }, { @@ -1141,6 +1243,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 'not used', + debugInfo: null, subHooks: [], }, ]); @@ -1165,6 +1268,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Optimistic', value: 'abc', + debugInfo: null, subHooks: [], }, { @@ -1172,6 +1276,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 'memo', + debugInfo: null, subHooks: [], }, { @@ -1179,6 +1284,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 'not used', + debugInfo: null, subHooks: [], }, ]); @@ -1205,6 +1311,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'FormState', value: 0, + debugInfo: null, subHooks: [], }, { @@ -1212,6 +1319,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 'memo', + debugInfo: null, subHooks: [], }, { @@ -1219,6 +1327,7 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'Memo', value: 'not used', + debugInfo: null, subHooks: [], }, ]); diff --git a/packages/react-devtools-shared/src/__tests__/inspectedElement-test.js b/packages/react-devtools-shared/src/__tests__/inspectedElement-test.js index 06410859d5640..a863c6144abcc 100644 --- a/packages/react-devtools-shared/src/__tests__/inspectedElement-test.js +++ b/packages/react-devtools-shared/src/__tests__/inspectedElement-test.js @@ -198,6 +198,7 @@ describe('InspectedElement', () => { "events": undefined, "hooks": [ { + "debugInfo": null, "hookSource": { "columnNumber": "removed by Jest serializer", "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js", @@ -240,6 +241,7 @@ describe('InspectedElement', () => { "events": undefined, "hooks": [ { + "debugInfo": null, "hookSource": { "columnNumber": "removed by Jest serializer", "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js", @@ -1157,6 +1159,7 @@ describe('InspectedElement', () => { expect(inspectedElement.hooks).toMatchInlineSnapshot(` [ { + "debugInfo": null, "hookSource": { "columnNumber": "removed by Jest serializer", "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js", @@ -1184,6 +1187,7 @@ describe('InspectedElement', () => { expect(inspectedElement.hooks).toMatchInlineSnapshot(` [ { + "debugInfo": null, "hookSource": { "columnNumber": "removed by Jest serializer", "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js", @@ -1659,6 +1663,7 @@ describe('InspectedElement', () => { "events": undefined, "hooks": [ { + "debugInfo": null, "hookSource": { "columnNumber": "removed by Jest serializer", "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js", @@ -1700,6 +1705,7 @@ describe('InspectedElement', () => { "events": undefined, "hooks": [ { + "debugInfo": null, "hookSource": { "columnNumber": "removed by Jest serializer", "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js", @@ -1945,6 +1951,7 @@ describe('InspectedElement', () => { expect(hooks).toMatchInlineSnapshot(` [ { + "debugInfo": null, "hookSource": { "columnNumber": "removed by Jest serializer", "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js", @@ -1956,6 +1963,7 @@ describe('InspectedElement', () => { "name": "DebuggableHook", "subHooks": [ { + "debugInfo": null, "hookSource": { "columnNumber": "removed by Jest serializer", "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js", @@ -2239,6 +2247,7 @@ describe('InspectedElement', () => { { "hooks": [ { + "debugInfo": null, "hookSource": { "columnNumber": "removed by Jest serializer", "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js", @@ -2275,6 +2284,7 @@ describe('InspectedElement', () => { { "hooks": [ { + "debugInfo": null, "hookSource": { "columnNumber": "removed by Jest serializer", "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js", @@ -2311,6 +2321,7 @@ describe('InspectedElement', () => { { "hooks": [ { + "debugInfo": null, "hookSource": { "columnNumber": "removed by Jest serializer", "fileName": "react-devtools-shared/src/__tests__/inspectedElement-test.js",