Repository: facebook/react
Pull request: DevTools: Add support for use(Context)

Stacked on https://github.com/facebook/react/pull/28232

## Summary

Add support for `use(Context)` Hook fixing "Support for `use()` not yet implemented in react-debug-tools." when inspecting components using `use(Context)`

`use(Promise)` will still throw: "Support for `use(Promise)` not yet implemented in react-debug-tools."

## How did you test this change?

- added test
- add usage to devtools-shell:
   ![Screenshot 2024-02-04 at 15 41 54](https://github.com/facebook/react/assets/12292047/33b55c74-8657-4689-b9dc-43912a81ec34)



Changed files:
filename: packages/react-debug-tools/src/ReactDebugHooks.js
patch: @@ -12,6 +12,7 @@ import type {
   ReactContext,
   ReactProviderType,
   StartTransitionOptions,
+  Usable,
 } from 'shared/ReactTypes';
 import type {
   Fiber,
@@ -27,7 +28,10 @@ import {
   ContextProvider,
   ForwardRef,
 } from 'react-reconciler/src/ReactWorkTags';
-import {REACT_MEMO_CACHE_SENTINEL} from 'shared/ReactSymbols';
+import {
+  REACT_MEMO_CACHE_SENTINEL,
+  REACT_CONTEXT_TYPE,
+} from 'shared/ReactSymbols';
 
 type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;
 
@@ -118,11 +122,30 @@ function readContext<T>(context: ReactContext<T>): T {
   return context._currentValue;
 }
 
-function use<T>(): T {
-  // TODO: What should this do if it receives an unresolved promise?
-  throw new Error(
-    'Support for `use` not yet implemented in react-debug-tools.',
-  );
+function use<T>(usable: Usable<T>): 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.',
+      );
+    } else if (usable.$$typeof === REACT_CONTEXT_TYPE) {
+      const context: ReactContext<T> = (usable: any);
+      const value = readContext(context);
+
+      hookLog.push({
+        primitive: 'Use',
+        stackError: new Error(),
+        value,
+      });
+
+      return value;
+    }
+  }
+
+  // eslint-disable-next-line react-internal/safe-string-coercion
+  throw new Error('An unsupported type was passed to use(): ' + String(usable));
 }
 
 function useContext<T>(context: ReactContext<T>): T {
@@ -660,7 +683,9 @@ function buildTree(
     // 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 === 'DebugValue'
+      primitive === 'Context' ||
+      primitive === 'DebugValue' ||
+      primitive === 'Use'
         ? null
         : nativeHookID++;
 

filename: packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js
patch: @@ -1108,6 +1108,44 @@ describe('ReactHooksInspectionIntegration', () => {
     ]);
   });
 
+  it('should support use(Context) hook', () => {
+    const Context = React.createContext('default');
+    function Foo() {
+      const value = React.use(Context);
+      React.useMemo(() => 'memo', []);
+      React.useMemo(() => 'not used', []);
+
+      return value;
+    }
+
+    const renderer = ReactTestRenderer.create(<Foo />);
+    const childFiber = renderer.root.findByType(Foo)._currentFiber();
+    const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
+    expect(tree).toEqual([
+      {
+        id: null,
+        isStateEditable: false,
+        name: 'Use',
+        value: 'default',
+        subHooks: [],
+      },
+      {
+        id: 0,
+        isStateEditable: false,
+        name: 'Memo',
+        value: 'memo',
+        subHooks: [],
+      },
+      {
+        id: 1,
+        isStateEditable: false,
+        name: 'Memo',
+        value: 'not used',
+        subHooks: [],
+      },
+    ]);
+  });
+
   // @gate enableAsyncActions
   it('should support useOptimistic hook', () => {
     const useOptimistic = React.useOptimistic;

filename: packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js
patch: @@ -19,6 +19,7 @@ import {
   useEffect,
   useOptimistic,
   useState,
+  use,
 } from 'react';
 import {useFormState} from 'react-dom';
 
@@ -76,6 +77,7 @@ function FunctionWithHooks(props: any, ref: React$Ref<any>) {
   // eslint-disable-next-line no-unused-vars
   const contextValueA = useContext(ContextA);
   useOptimistic<number, mixed>(1);
+  use(ContextA);
 
   // eslint-disable-next-line no-unused-vars
   const [_, __] = useState(object);