DevTools: Add support for useOptimistic Hook (#27982)
## Summary Add support for `useOptimistic` Hook fixing "Unsupported hook in the react-debug-tools package: Missing method in Dispatcher: useOptimistic" when inspecting components using `useOptimistic` ## How did you test this change? - Added test following the same pattern as for `useDeferredValue`
Sebastian Silbermann committed
Feb 2, 2024 at 23:18 UTC
85cc01743bc992a689770a4f37e3d5441f14f082
3 files changed
+65
packages/react-debug-tools/src/ReactDebugHooks.js
+25
@@ -80,6 +80,11 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
80
// This type check is for Flow only.
81
Dispatcher.useMemoCache(0);
82
}
83
+
84
+ if (typeof Dispatcher.useOptimistic === 'function') {
85
+ // This type check is for Flow only.
86
+ Dispatcher.useOptimistic(null, (s: mixed, a: mixed) => s);
87
+ }
88
} finally {
89
readHookLog = hookLog;
90
hookLog = [];
@@ -348,6 +353,25 @@ function useMemoCache(size: number): Array<any> {
353
return data;
354
}
355
356
+function useOptimistic<S, A>(
357
+ passthrough: S,
358
+ reducer: ?(S, A) => S,
359
+): [S, (A) => void] {
360
+ const hook = nextHook();
361
+ let state;
362
+ if (hook !== null) {
363
+ state = hook.memoizedState;
364
+ } else {
365
+ state = passthrough;
366
+ }
367
+ hookLog.push({
368
+ primitive: 'Optimistic',
369
+ stackError: new Error(),
370
+ value: state,
371
+ });
372
+ return [state, (action: A) => {}];
373
+}
374
+
375
const Dispatcher: DispatcherType = {
376
use,
377
readContext,
@@ -361,6 +385,7 @@ const Dispatcher: DispatcherType = {
385
useInsertionEffect,
386
useMemo,
387
useMemoCache,
388
+ useOptimistic,
389
useReducer,
390
useRef,
391
useState,
packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js
+38
@@ -1106,4 +1106,42 @@ describe('ReactHooksInspectionIntegration', () => {
1106
},
1107
]);
1108
});
1109
+
1110
+ // @gate enableAsyncActions
1111
+ it('should support useOptimistic hook', () => {
1112
+ const useOptimistic = React.useOptimistic;
1113
+ function Foo() {
1114
+ const [value] = useOptimistic('abc', currentState => currentState);
1115
+ React.useMemo(() => 'memo', []);
1116
+ React.useMemo(() => 'not used', []);
1117
+ return value;
1118
+ }
1119
+
1120
+ const renderer = ReactTestRenderer.create(<Foo />);
1121
+ const childFiber = renderer.root.findByType(Foo)._currentFiber();
1122
+ const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
1123
+ expect(tree).toEqual([
1124
+ {
1125
+ id: 0,
1126
+ isStateEditable: false,
1127
+ name: 'Optimistic',
1128
+ value: 'abc',
1129
+ subHooks: [],
1130
+ },
1131
+ {
1132
+ id: 1,
1133
+ isStateEditable: false,
1134
+ name: 'Memo',
1135
+ value: 'memo',
1136
+ subHooks: [],
1137
+ },
1138
+ {
1139
+ id: 2,
1140
+ isStateEditable: false,
1141
+ name: 'Memo',
1142
+ value: 'not used',
1143
+ subHooks: [],
1144
+ },
1145
+ ]);
1146
+ });
1147
});
packages/react-devtools-shell/src/app/InspectableElements/CustomHooks.js
+2
@@ -17,6 +17,7 @@ import {
17
useContext,
18
useDebugValue,
19
useEffect,
20
+ useOptimistic,
21
useState,
22
} from 'react';
23
@@ -73,6 +74,7 @@ function FunctionWithHooks(props: any, ref: React$Ref<any>) {
74
const [count, updateCount] = useState(0);
75
// eslint-disable-next-line no-unused-vars
76
const contextValueA = useContext(ContextA);
77
+ useOptimistic<number, mixed>(1);
78
79
// eslint-disable-next-line no-unused-vars
80
const [_, __] = useState(object);