Skip to content

Commit

Permalink
DevTools: Add support for use(Context)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian Silbermann committed Feb 7, 2024
1 parent bfdc12c commit 4955d0d
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 7 deletions.
43 changes: 36 additions & 7 deletions packages/react-debug-tools/src/ReactDebugHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
ReactContext,
ReactProviderType,
StartTransitionOptions,
Usable,
} from 'shared/ReactTypes';
import type {
Fiber,
Expand All @@ -27,7 +28,11 @@ import {
ContextProvider,
ForwardRef,
} from 'react-reconciler/src/ReactWorkTags';
import {REACT_MEMO_CACHE_SENTINEL} from 'shared/ReactSymbols';
import {
REACT_MEMO_CACHE_SENTINEL,
REACT_CONTEXT_TYPE,
REACT_SERVER_CONTEXT_TYPE,
} from 'shared/ReactSymbols';

type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;

Expand Down Expand Up @@ -118,11 +123,33 @@ 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 ||
usable.$$typeof === REACT_SERVER_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 {
Expand Down Expand Up @@ -660,7 +687,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++;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
useEffect,
useOptimistic,
useState,
use,
} from 'react';
import {useFormState} from 'react-dom';

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 4955d0d

Please sign in to comment.