mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
DevTools: Add support for useFormState (#28232)
## Summary Add support for `useFormState` Hook fixing "Unsupported hook in the react-debug-tools package: Missing method in Dispatcher: useFormState" when inspecting components using `useFormState` ## How did you test this change? - Added test to ReactHooksInspectionIntegration - Added dedicated section for form actions to devtools-shell 
This commit is contained in:
committed by
GitHub
parent
214fe84f53
commit
56cd10beb4
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {
|
||||
Awaited,
|
||||
ReactContext,
|
||||
ReactProviderType,
|
||||
StartTransitionOptions,
|
||||
@@ -80,11 +81,14 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
|
||||
// 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);
|
||||
}
|
||||
if (typeof Dispatcher.useFormState === 'function') {
|
||||
// This type check is for Flow only.
|
||||
Dispatcher.useFormState((s: mixed, p: mixed) => s, null);
|
||||
}
|
||||
} finally {
|
||||
readHookLog = hookLog;
|
||||
hookLog = [];
|
||||
@@ -372,6 +376,27 @@ function useOptimistic<S, A>(
|
||||
return [state, (action: A) => {}];
|
||||
}
|
||||
|
||||
function useFormState<S, P>(
|
||||
action: (Awaited<S>, P) => S,
|
||||
initialState: Awaited<S>,
|
||||
permalink?: string,
|
||||
): [Awaited<S>, (P) => void] {
|
||||
const hook = nextHook(); // FormState
|
||||
nextHook(); // ActionQueue
|
||||
let state;
|
||||
if (hook !== null) {
|
||||
state = hook.memoizedState;
|
||||
} else {
|
||||
state = initialState;
|
||||
}
|
||||
hookLog.push({
|
||||
primitive: 'FormState',
|
||||
stackError: new Error(),
|
||||
value: state,
|
||||
});
|
||||
return [state, (payload: P) => {}];
|
||||
}
|
||||
|
||||
const Dispatcher: DispatcherType = {
|
||||
use,
|
||||
readContext,
|
||||
@@ -393,6 +418,7 @@ const Dispatcher: DispatcherType = {
|
||||
useSyncExternalStore,
|
||||
useDeferredValue,
|
||||
useId,
|
||||
useFormState,
|
||||
};
|
||||
|
||||
// create a proxy to throw a custom error
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
'use strict';
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactTestRenderer;
|
||||
let ReactDebugTools;
|
||||
let act;
|
||||
@@ -21,6 +22,7 @@ describe('ReactHooksInspectionIntegration', () => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactTestRenderer = require('react-test-renderer');
|
||||
ReactDOM = require('react-dom');
|
||||
act = require('internal-test-utils').act;
|
||||
ReactDebugTools = require('react-debug-tools');
|
||||
useMemoCache = React.unstable_useMemoCache;
|
||||
@@ -1144,4 +1146,44 @@ describe('ReactHooksInspectionIntegration', () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate enableFormActions && enableAsyncActions
|
||||
it('should support useFormState hook', () => {
|
||||
function Foo() {
|
||||
const [value] = ReactDOM.useFormState(function increment(n) {
|
||||
return n;
|
||||
}, 0);
|
||||
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: 0,
|
||||
isStateEditable: false,
|
||||
name: 'FormState',
|
||||
value: 0,
|
||||
subHooks: [],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
isStateEditable: false,
|
||||
name: 'Memo',
|
||||
value: 'memo',
|
||||
subHooks: [],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
isStateEditable: false,
|
||||
name: 'Memo',
|
||||
value: 'not used',
|
||||
subHooks: [],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
useOptimistic,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {useFormState} from 'react-dom';
|
||||
|
||||
const object = {
|
||||
string: 'abc',
|
||||
@@ -117,6 +118,18 @@ function wrapWithHoc(Component: (props: any, ref: React$Ref<any>) => any) {
|
||||
}
|
||||
const HocWithHooks = wrapWithHoc(FunctionWithHooks);
|
||||
|
||||
function Forms() {
|
||||
const [state, formAction] = useFormState((n: number, formData: FormData) => {
|
||||
return n + 1;
|
||||
}, 0);
|
||||
return (
|
||||
<form>
|
||||
{state}
|
||||
<button formAction={formAction}>Increment</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default function CustomHooks(): React.Node {
|
||||
return (
|
||||
<Fragment>
|
||||
@@ -124,6 +137,7 @@ export default function CustomHooks(): React.Node {
|
||||
<MemoWithHooks />
|
||||
<ForwardRefWithHooks />
|
||||
<HocWithHooks />
|
||||
<Forms />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user