main
js 84 lines 2.64 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
11 import type {Awaited} from 'shared/ReactTypes';
12
13 import ReactSharedInternals from 'shared/ReactSharedInternals';
14 import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
15
16 type FormStatusNotPending = {|
17 pending: false,
18 data: null,
19 method: null,
20 action: null,
21 |};
22
23 type FormStatusPending = {|
24 pending: true,
25 data: FormData,
26 method: string,
27 action: string | (FormData => void | Promise<void>) | null,
28 |};
29
30 export type FormStatus = FormStatusPending | FormStatusNotPending;
31
32 // Since the "not pending" value is always the same, we can reuse the
33 // same object across all transitions.
34 const sharedNotPendingObject: FormStatusNotPending = {
35 pending: false,
36 data: null,
37 method: null,
38 action: null,
39 };
40
41 export const NotPending: FormStatus = __DEV__
42 ? Object.freeze(sharedNotPendingObject)
43 : sharedNotPendingObject;
44
45 function resolveDispatcher() {
46 // Copied from react/src/ReactHooks.js. It's the same thing but in a
47 // different package.
48 const dispatcher = ReactSharedInternals.H;
49 if (__DEV__) {
50 if (dispatcher === null) {
51 console.error(
52 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
53 ' one of the following reasons:\n' +
54 '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
55 '2. You might be breaking the Rules of Hooks\n' +
56 '3. You might have more than one copy of React in the same app\n' +
57 'See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.',
58 );
59 }
60 }
61 // Will result in a null access error if accessed outside render phase. We
62 // intentionally don't throw our own error because this is in a hot path.
63 // Also helps ensure this is inlined.
64 return dispatcher as any as Dispatcher;
65 }
66
67 export function useFormStatus(): FormStatus {
68 const dispatcher = resolveDispatcher();
69 return dispatcher.useHostTransitionStatus();
70 }
71
72 export function useFormState<S, P>(
73 action: (Awaited<S>, P) => S,
74 initialState: Awaited<S>,
75 permalink?: string,
76 ): [Awaited<S>, (P) => void, boolean] {
77 const dispatcher = resolveDispatcher();
78 return dispatcher.useFormState(action, initialState, permalink);
79 }
80
81 export function requestFormReset(form: HTMLFormElement) {
82 ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */
83 .r(/* requestFormReset */ form);
84 }