diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 111041548259b..9eab7a4a6b2e9 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -80,6 +80,11 @@ function getPrimitiveStackCache(): Map> { // This type check is for Flow only. Dispatcher.useMemoCache(0); } + + if (typeof Dispatcher.useOptimistic === 'function') { + // This type check is for Flow only. + Dispatcher.useOptimistic(null, (s: mixed, a: mixed) => s); + } } finally { readHookLog = hookLog; hookLog = []; @@ -348,6 +353,25 @@ function useMemoCache(size: number): Array { return data; } +function useOptimistic( + passthrough: S, + reducer: ?(S, A) => S, +): [S, (A) => void] { + const hook = nextHook(); + let state; + if (hook !== null) { + state = hook.memoizedState; + } else { + state = passthrough; + } + hookLog.push({ + primitive: 'Optimistic', + stackError: new Error(), + value: state, + }); + return [state, (action: A) => {}]; +} + const Dispatcher: DispatcherType = { use, readContext, @@ -361,6 +385,7 @@ const Dispatcher: DispatcherType = { useInsertionEffect, useMemo, useMemoCache, + useOptimistic, useReducer, useRef, useState, diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js index e220939a81e8b..9e1b4c35fe4af 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js @@ -1106,4 +1106,42 @@ describe('ReactHooksInspectionIntegration', () => { }, ]); }); + + // @gate enableAsyncActions + it('should support useOptimistic hook', () => { + const useOptimistic = React.useOptimistic; + function Foo() { + const [value] = useOptimistic('abc', currentState => currentState); + React.useMemo(() => 'memo', []); + React.useMemo(() => 'not used', []); + return value; + } + + const renderer = ReactTestRenderer.create(); + const childFiber = renderer.root.findByType(Foo)._currentFiber(); + const tree = ReactDebugTools.inspectHooksOfFiber(childFiber); + expect(tree).toEqual([ + { + id: 0, + isStateEditable: false, + name: 'Optimistic', + value: 'abc', + subHooks: [], + }, + { + id: 1, + isStateEditable: false, + name: 'Memo', + value: 'memo', + subHooks: [], + }, + { + id: 2, + isStateEditable: false, + name: 'Memo', + value: 'not used', + subHooks: [], + }, + ]); + }); }); diff --git a/packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js b/packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js index 939b2fa20e0dd..0c761b4a47136 100644 --- a/packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js +++ b/packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js @@ -17,6 +17,7 @@ import { useContext, useDebugValue, useEffect, + useOptimistic, useState, } from 'react'; @@ -73,6 +74,7 @@ function FunctionWithHooks(props: any, ref: React$Ref) { const [count, updateCount] = useState(0); // eslint-disable-next-line no-unused-vars const contextValueA = useContext(ContextA); + useOptimistic(1); // eslint-disable-next-line no-unused-vars const [_, __] = useState(object);