@samitouri / QOS-React-1 / commits / 04b59928d8

DevTools: Add support for use(Context) (#28233)

Sebastian Silbermann committed Feb 8, 2024 at 18:47 UTC 04b59928d867dae1639f12f19700347d8f5d4cac
3 files changed +72 -7
packages/react-debug-tools/src/ReactDebugHooks.js
+32 -7
@@ -12,6 +12,7 @@ import type {
12 ReactContext,
13 ReactProviderType,
14 StartTransitionOptions,
15 + Usable,
16 } from 'shared/ReactTypes';
17 import type {
18 Fiber,
@@ -27,7 +28,10 @@ import {
28 ContextProvider,
29 ForwardRef,
30 } from 'react-reconciler/src/ReactWorkTags';
30 -import {REACT_MEMO_CACHE_SENTINEL} from 'shared/ReactSymbols';
31 +import {
32 + REACT_MEMO_CACHE_SENTINEL,
33 + REACT_CONTEXT_TYPE,
34 +} from 'shared/ReactSymbols';
35
36 type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;
37
@@ -118,11 +122,30 @@ function readContext<T>(context: ReactContext<T>): T {
122 return context._currentValue;
123 }
124
121 -function use<T>(): T {
122 - // TODO: What should this do if it receives an unresolved promise?
123 - throw new Error(
124 - 'Support for `use` not yet implemented in react-debug-tools.',
125 - );
125 +function use<T>(usable: Usable<T>): T {
126 + if (usable !== null && typeof usable === 'object') {
127 + // $FlowFixMe[method-unbinding]
128 + if (typeof usable.then === 'function') {
129 + // TODO: What should this do if it receives an unresolved promise?
130 + throw new Error(
131 + 'Support for `use(Promise)` not yet implemented in react-debug-tools.',
132 + );
133 + } else if (usable.$$typeof === REACT_CONTEXT_TYPE) {
134 + const context: ReactContext<T> = (usable: any);
135 + const value = readContext(context);
136 +
137 + hookLog.push({
138 + primitive: 'Use',
139 + stackError: new Error(),
140 + value,
141 + });
142 +
143 + return value;
144 + }
145 + }
146 +
147 + // eslint-disable-next-line react-internal/safe-string-coercion
148 + throw new Error('An unsupported type was passed to use(): ' + String(usable));
149 }
150
151 function useContext<T>(context: ReactContext<T>): T {
@@ -660,7 +683,9 @@ function buildTree(
683 // For now, the "id" of stateful hooks is just the stateful hook index.
684 // Custom hooks have no ids, nor do non-stateful native hooks (e.g. Context, DebugValue).
685 const id =
663 - primitive === 'Context' || primitive === 'DebugValue'
686 + primitive === 'Context' ||
687 + primitive === 'DebugValue' ||
688 + primitive === 'Use'
689 ? null
690 : nativeHookID++;
691
packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js
+38
@@ -1108,6 +1108,44 @@ describe('ReactHooksInspectionIntegration', () => {
1108 ]);
1109 });
1110
1111 + it('should support use(Context) hook', () => {
1112 + const Context = React.createContext('default');
1113 + function Foo() {
1114 + const value = React.use(Context);
1115 + React.useMemo(() => 'memo', []);
1116 + React.useMemo(() => 'not used', []);
1117 +
1118 + return value;
1119 + }
1120 +
1121 + const renderer = ReactTestRenderer.create(<Foo />);
1122 + const childFiber = renderer.root.findByType(Foo)._currentFiber();
1123 + const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
1124 + expect(tree).toEqual([
1125 + {
1126 + id: null,
1127 + isStateEditable: false,
1128 + name: 'Use',
1129 + value: 'default',
1130 + subHooks: [],
1131 + },
1132 + {
1133 + id: 0,
1134 + isStateEditable: false,
1135 + name: 'Memo',
1136 + value: 'memo',
1137 + subHooks: [],
1138 + },
1139 + {
1140 + id: 1,
1141 + isStateEditable: false,
1142 + name: 'Memo',
1143 + value: 'not used',
1144 + subHooks: [],
1145 + },
1146 + ]);
1147 + });
1148 +
1149 // @gate enableAsyncActions
1150 it('should support useOptimistic hook', () => {
1151 const useOptimistic = React.useOptimistic;
packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js
+2
@@ -19,6 +19,7 @@ import {
19 useEffect,
20 useOptimistic,
21 useState,
22 + use,
23 } from 'react';
24 import {useFormState} from 'react-dom';
25
@@ -76,6 +77,7 @@ function FunctionWithHooks(props: any, ref: React$Ref<any>) {
77 // eslint-disable-next-line no-unused-vars
78 const contextValueA = useContext(ContextA);
79 useOptimistic<number, mixed>(1);
80 + use(ContextA);
81
82 // eslint-disable-next-line no-unused-vars
83 const [_, __] = useState(object);