taro-form-react 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,441 @@
1
+ import React, { createContext, useCallback, useContext, useState } from "react";
2
+
3
+ import { cloneDeep, get, merge, set, unset } from "lodash-es";
4
+
5
+ import useMap from "@/hooks/useMap";
6
+
7
+ import type { FormLabelProps } from "../components/Label";
8
+ import type { NamePath } from "../types";
9
+
10
+ import { namePathToString } from "../utils";
11
+
12
+ export type FieldTransformResult = {
13
+ __form_internals_should_merge?: boolean;
14
+ value: any;
15
+ };
16
+
17
+ export type FieldRefActions = {
18
+ validate: () => (void | string[] | Promise<void | string[]>);
19
+ // transform 会将返回的对象 merge 到整个表单的 data 中
20
+ // 如果设置了 transform 且 __form_internals_should_merge 为 true
21
+ // 那么 field 的 name 会被忽略(仅在使用 getFieldsFormattedValue 时有效)
22
+ // 否则会直接使用返回的值
23
+ transform?: () => FieldTransformResult | Promise<FieldTransformResult>;
24
+ };
25
+ export type FieldRef = React.RefObject<FieldRefActions>;
26
+
27
+ export type Field = {
28
+ refs: FieldRef[];
29
+ name: NamePath;
30
+ touched: boolean;
31
+ errors: string[];
32
+ initialValue?: any;
33
+ count: number;
34
+ };
35
+
36
+ export type FormContextProps = {
37
+ initialValues?: Record<string, any>;
38
+
39
+ showError?: boolean;
40
+ colon?: boolean;
41
+ labelProps?: FormLabelProps;
42
+ layout?: "horizontal" | "vertical";
43
+ validateFirst?: boolean | "parallel";
44
+ showErrors?: boolean;
45
+ passthroughErrors?: boolean;
46
+ // merge 会将 transform 返回的对象 merge 到整个表单的 data 中
47
+ // replace 会直接使用 transform 返回的值填充到对应的 field 中
48
+ transformBehavior?: "merge" | "replace";
49
+ getRequiredMessage?: (label: string) => string;
50
+
51
+ data: Record<string, any>;
52
+ setData: (data: Record<string, any>) => void;
53
+
54
+ fields: Map<string, Field>;
55
+ registerField: (name: NamePath, ref: FieldRef, initialValue?: any) => void;
56
+ unregisterField: (name: NamePath, ref: FieldRef) => void;
57
+
58
+ setFieldValue: (name: NamePath, value: any) => void;
59
+ getFieldValue: (name: NamePath) => any;
60
+ getFieldsValue: (nameList?: NamePath[]) => Record<string, any>;
61
+ getFieldsFormattedValue: (nameList?: NamePath[]) => Promise<Record<string, any>>;
62
+
63
+ setFields: (
64
+ fields: Array<
65
+ Pick<Field, "name">
66
+ & { touched?: boolean; }
67
+ & { value?: any }
68
+ >
69
+ ) => void;
70
+ getFields: (nameList?: NamePath[]) => Array<
71
+ Pick<Field, "name" | "touched" | "errors">
72
+ & { value: any }
73
+ >;
74
+
75
+ resetFields: (nameList?: NamePath[]) => void;
76
+
77
+ setFieldError: (name: NamePath, errors: string[]) => void;
78
+ getFieldError: (name: NamePath) => string[];
79
+
80
+ validateFields: (nameList?: NamePath[]) => Promise<void | Pick<Field, "name" | "errors">[]>;
81
+
82
+ isFieldsTouched: (nameList?: NamePath[], options?: { allTouched?: boolean }) => boolean;
83
+ };
84
+
85
+ const FormContext = createContext<FormContextProps | undefined>(void 0);
86
+
87
+ export const useFormContext = () => {
88
+ const context = useContext(FormContext);
89
+ if (!context) {
90
+ throw new Error("useFormContext must be used within a FormProvider");
91
+ }
92
+ return context;
93
+ };
94
+
95
+ export type FormProviderConfiguration = Pick<FormContextProps, "colon" | "labelProps" | "layout" | "validateFirst" | "showErrors" | "passthroughErrors" | "transformBehavior" | "getRequiredMessage">;
96
+
97
+ export type FormProviderProps =
98
+ & FormProviderConfiguration
99
+ & Pick<FormContextProps, "initialValues">
100
+ & {
101
+ onFieldsChange?: (changedFields: Array<{ name: NamePath; value: any }>, allFields: Array<{ name: NamePath; value: any }>) => void;
102
+ onValuesChange?: (changedValues: Record<string, any>, allValues: Record<string, any>) => void;
103
+ children: React.ReactNode;
104
+ };
105
+
106
+ export const FormContextProvider: React.FC<FormProviderProps> = ({
107
+ initialValues,
108
+ onFieldsChange,
109
+ onValuesChange,
110
+ children,
111
+ ...props
112
+ }) => {
113
+ const [fields, { set: setField, get: getField, remove: removeField }] = useMap<string, Field>();
114
+ const [data, setData] = useState<Record<string, any>>({});
115
+
116
+ const registerField = useCallback<FormContextProps["registerField"]>((name, ref, initialValue) => {
117
+ const nameString = namePathToString(name);
118
+ const value = initialValue ?? get(initialValues, name);
119
+ setData(data => {
120
+ const nextData = cloneDeep(data);
121
+ set(nextData, name, value);
122
+ return nextData;
123
+ });
124
+
125
+ const field = getField(nameString);
126
+ if (field) {
127
+ console.warn(`Field "${nameString}" is already registered, register multiple times may cause unexpected behavior.`);
128
+ }
129
+ const fieldToSet = field
130
+ ? {
131
+ ...field,
132
+ refs: [...field.refs, ref],
133
+ initialValue,
134
+ count: field.count + 1,
135
+ }
136
+ : {
137
+ refs: [ref],
138
+ name,
139
+ touched: false,
140
+ errors: [],
141
+ initialValue,
142
+ count: 1,
143
+ };
144
+ setField(nameString, fieldToSet);
145
+
146
+ onFieldsChange?.(
147
+ [{ name, value }],
148
+ Array.from(fields.values()).map(field => ({ name: field.name, value: get(data, field.name) })).concat([{ name, value }]),
149
+ );
150
+ }, [data, fields, getField, initialValues, onFieldsChange, setField]);
151
+
152
+ const unregisterField = useCallback<FormContextProps["unregisterField"]>((name, ref) => {
153
+ const nameString = namePathToString(name);
154
+ const field = getField(nameString);
155
+ if (field) {
156
+ if (field.count === 1) {
157
+ removeField(nameString);
158
+ setData(data => {
159
+ const nextData = cloneDeep(data);
160
+ unset(nextData, name);
161
+ return nextData;
162
+ });
163
+ onFieldsChange?.(
164
+ [{ name, value: get(data, name) }],
165
+ Array.from(fields.values()).map(field => ({ name: field.name, value: get(data, field.name) })).filter(f => f.name !== name),
166
+ );
167
+ } else {
168
+ setField(nameString, {
169
+ ...field,
170
+ count: field.count - 1,
171
+ refs: field.refs.filter(r => r !== ref),
172
+ });
173
+ }
174
+ } else {
175
+ console.warn(`Attempted to unregister field "${nameString}" that was never registered.`);
176
+ }
177
+ }, [data, fields, getField, onFieldsChange, removeField, setField]);
178
+
179
+ const setFieldValue = useCallback<FormContextProps["setFieldValue"]>((name, value) => {
180
+ const nameString = namePathToString(name);
181
+ const field = getField(nameString);
182
+ if (!field) {
183
+ console.warn(`Attempted to set value for field "${nameString}" that was never registered.`);
184
+ return;
185
+ }
186
+
187
+ setData(data => {
188
+ const nextData = cloneDeep(data);
189
+ set(nextData, name, value);
190
+ return nextData;
191
+ });
192
+
193
+ setField(nameString, { ...field, touched: true });
194
+
195
+ if (onValuesChange) {
196
+ const changes = {};
197
+ set(changes, name, value);
198
+ const all = cloneDeep(data);
199
+ set(all, name, value);
200
+ onValuesChange(changes, all);
201
+ }
202
+ }, [data, getField, onValuesChange, setField]);
203
+
204
+ const getFieldValue = useCallback<FormContextProps["getFieldValue"]>(name => {
205
+ return get(data, name);
206
+ }, [data]);
207
+
208
+ const getFieldsValue = useCallback<FormContextProps["getFieldsValue"]>(nameList => {
209
+ if (!nameList) return data;
210
+
211
+ const result = {} as Record<string, any>;
212
+ for (const name of nameList) {
213
+ const value = get(data, name);
214
+ set(result, name, value);
215
+ }
216
+ return result;
217
+ }, [data]);
218
+
219
+ const getFieldsFormattedValue = useCallback<FormContextProps["getFieldsFormattedValue"]>(async nameList => {
220
+ const realNameList = nameList ?? Array.from(fields.values()).map(field => field.name);
221
+
222
+ const result = {} as Record<string, any>;
223
+
224
+ for (const name of realNameList) {
225
+ const field = getField(namePathToString(name));
226
+ if (!field) {
227
+ console.warn(`Attempted to get formatted value for field "${namePathToString(name)}" that was never registered.`);
228
+ continue;
229
+ }
230
+
231
+ if (
232
+ field.refs.some(ref => !ref.current)
233
+ ) {
234
+ console.warn(`Attempted to get formatted value for field "${namePathToString(name)}" that has no or missing ref.`);
235
+ continue;
236
+ }
237
+
238
+ // only consider the last ref's transform result
239
+ const transformFunc = field.refs[field.refs.length - 1].current!.transform;
240
+ if (!transformFunc) {
241
+ set(result, name, get(data, name));
242
+ } else {
243
+ const transformedValue = await transformFunc();
244
+ if (transformedValue.__form_internals_should_merge) {
245
+ merge(result, transformedValue.value);
246
+ } else {
247
+ set(result, name, transformedValue);
248
+ }
249
+ }
250
+ }
251
+
252
+ return result;
253
+ }, [data, fields, getField]);
254
+
255
+ const setFields = useCallback<FormContextProps["setFields"]>(fields => {
256
+ const changes = {};
257
+ for (const fieldData of fields) {
258
+ const nameString = namePathToString(fieldData.name);
259
+ const field = getField(namePathToString(fieldData.name));
260
+ if (!field) {
261
+ console.warn(`Attempted to set field "${nameString}" that was never registered.`);
262
+ continue;
263
+ }
264
+
265
+ if (fieldData.touched !== undefined) {
266
+ setField(nameString, { ...field, touched: fieldData.touched });
267
+ }
268
+
269
+ setData(data => {
270
+ const nextData = cloneDeep(data);
271
+ set(nextData, fieldData.name, fieldData.value);
272
+ set(changes, fieldData.name, fieldData.value);
273
+ return nextData;
274
+ });
275
+ }
276
+ onValuesChange?.(changes, merge(cloneDeep(data), changes));
277
+ }, [data, getField, onValuesChange, setField]);
278
+
279
+ const getFields = useCallback<FormContextProps["getFields"]>(nameList => {
280
+ if (!nameList) {
281
+ return Array
282
+ .from(fields.values())
283
+ .map(field => ({ ...field, value: get(data, field.name) }));
284
+ }
285
+
286
+ const result = [] as ReturnType<FormContextProps["getFields"]>;
287
+ for (const name of nameList) {
288
+ const field = getField(namePathToString(name));
289
+ if (!field) {
290
+ console.warn(`Attempted to get field "${namePathToString(name)}" that was never registered.`);
291
+ continue;
292
+ }
293
+
294
+ const value = get(data, name);
295
+ result.push({ ...field, value });
296
+ }
297
+ return result;
298
+ }, [data, fields, getField]);
299
+
300
+ const resetFields = useCallback<FormContextProps["resetFields"]>(nameList => {
301
+ const fieldsToReset =
302
+ nameList
303
+ ?.map(name => {
304
+ const nameString = namePathToString(name);
305
+ const field = getField(nameString);
306
+ if (!field) {
307
+ console.warn(`Attempted to reset field "${nameString}" that was never registered.`);
308
+ return undefined;
309
+ }
310
+ return field;
311
+ })
312
+ .filter(v => v !== undefined)
313
+ ?? Array.from(fields.values());
314
+
315
+ for (const field of fieldsToReset) {
316
+ setData(data => {
317
+ const nextData = cloneDeep(data);
318
+ set(nextData, field.name, field.initialValue ?? get(initialValues, field.name));
319
+ return nextData;
320
+ });
321
+
322
+ setField(namePathToString(field.name), { ...field, touched: false });
323
+ }
324
+ }, [fields, getField, initialValues, setField]);
325
+
326
+ const setFieldError = useCallback<FormContextProps["setFieldError"]>((name, errors) => {
327
+ const nameString = namePathToString(name);
328
+ const field = getField(nameString);
329
+ if (!field) {
330
+ console.warn(`Attempted to set errors for field "${nameString}" that was never registered.`);
331
+ return;
332
+ }
333
+
334
+ setField(nameString, { ...field, errors });
335
+ }, [getField, setField]);
336
+
337
+ const getFieldError = useCallback<FormContextProps["getFieldError"]>(name => {
338
+ const field = getField(namePathToString(name));
339
+ if (!field) {
340
+ console.warn(`Attempted to get errors for field "${namePathToString(name)}" that was never registered.`);
341
+ return [];
342
+ }
343
+ return field.errors;
344
+ }, [getField]);
345
+
346
+ const validateFields = useCallback<FormContextProps["validateFields"]>(async nameList => {
347
+ const fieldsToValidate =
348
+ nameList
349
+ ?.map(name => {
350
+ const nameString = namePathToString(name);
351
+ const field = getField(nameString);
352
+ if (!field) {
353
+ console.warn(`Attempted to validate field "${nameString}" that was never registered.`);
354
+ return undefined;
355
+ }
356
+ return field;
357
+ })
358
+ .filter(v => v !== undefined)
359
+ ?? Array.from(fields.values());
360
+
361
+ const errors = [] as Pick<Field, "name" | "errors">[];
362
+
363
+ for (const field of fieldsToValidate) {
364
+ if (
365
+ field.refs.some(ref => !ref.current)
366
+ ) {
367
+ console.warn(`Attempted to validate field "${namePathToString(field.name)}" that has no or missing ref.`);
368
+ continue;
369
+ }
370
+
371
+ const result = await Promise.all(field.refs.map(ref => ref.current!.validate()));
372
+ const resultErrors = Array.from(new Set(result.flat().filter(Boolean))) as string[];
373
+ if (resultErrors.length > 0) {
374
+ errors.push({
375
+ name: field.name,
376
+ errors: resultErrors,
377
+ });
378
+ }
379
+ }
380
+
381
+ return errors.length > 0 ? errors : void 0;
382
+ }, [fields, getField]);
383
+
384
+ const isFieldsTouched = useCallback<FormContextProps["isFieldsTouched"]>((nameList, options) => {
385
+ const { allTouched = false } = options ?? {};
386
+
387
+ const fieldsToCheck =
388
+ nameList
389
+ ?.map(name => {
390
+ const nameString = namePathToString(name);
391
+ const field = getField(nameString);
392
+ if (!field) {
393
+ console.warn(`Attempted to check if field "${nameString}" is touched but it was never registered.`);
394
+ return undefined;
395
+ }
396
+ return field;
397
+ })
398
+ .filter(v => v !== undefined)
399
+ ?? Array.from(fields.values());
400
+
401
+ return allTouched
402
+ ? fieldsToCheck.every(field => field.touched)
403
+ : fieldsToCheck.some(field => field.touched);
404
+ }, [fields, getField]);
405
+
406
+ return (
407
+ <FormContext.Provider
408
+ value={{
409
+ initialValues,
410
+
411
+ ...props,
412
+
413
+ data,
414
+ setData,
415
+
416
+ fields,
417
+ registerField,
418
+ unregisterField,
419
+
420
+ setFieldValue,
421
+ getFieldValue,
422
+ getFieldsValue,
423
+ getFieldsFormattedValue,
424
+
425
+ setFields,
426
+ getFields,
427
+
428
+ resetFields,
429
+
430
+ setFieldError,
431
+ getFieldError,
432
+
433
+ validateFields,
434
+
435
+ isFieldsTouched,
436
+ }}
437
+ >
438
+ {children}
439
+ </FormContext.Provider>
440
+ );
441
+ };
@@ -0,0 +1,37 @@
1
+ import type { FormProviderConfiguration } from "../context/FormContext";
2
+
3
+ import { useFormContext } from "../context/FormContext";
4
+
5
+ const fallback = (value: any, fallbackValue: any) => (value === undefined ? fallbackValue : value);
6
+
7
+ export default function useFormConfiguration ({
8
+ colon,
9
+ labelProps,
10
+ layout,
11
+ validateFirst,
12
+ passthroughErrors,
13
+ showErrors,
14
+ getRequiredMessage,
15
+ }: Partial<FormProviderConfiguration> = {}) {
16
+ const {
17
+ colon: contextColon = true,
18
+ labelProps: contextLabelProps = {},
19
+ layout: contextLayout = "vertical",
20
+ validateFirst: contextValidateFirst = false,
21
+ passthroughErrors: contextPassthroughErrors = false,
22
+ showErrors: contextShowErrors = true,
23
+ transformBehavior: contextTransformBehavior = "replace",
24
+ getRequiredMessage: contextGetRequiredMessage = label => `${label}不能为空`,
25
+ } = useFormContext();
26
+
27
+ return {
28
+ colon: fallback(colon, contextColon),
29
+ labelProps: fallback(labelProps, contextLabelProps),
30
+ layout: fallback(layout, contextLayout),
31
+ validateFirst: fallback(validateFirst, contextValidateFirst),
32
+ passthroughErrors: fallback(passthroughErrors, contextPassthroughErrors),
33
+ showErrors: fallback(showErrors, contextShowErrors),
34
+ transformBehavior: contextTransformBehavior,
35
+ getRequiredMessage: fallback(getRequiredMessage, contextGetRequiredMessage),
36
+ };
37
+ }
@@ -0,0 +1,75 @@
1
+ import { useCallback, useMemo, useRef } from "react";
2
+
3
+ import { useDeepCompareEffect } from "ahooks";
4
+ import { get } from "lodash-es";
5
+
6
+ import type { FieldRef } from "../context/FormContext";
7
+ import type { NamePath } from "../types";
8
+
9
+ import { useFormContext } from "../context/FormContext";
10
+
11
+ export type UseFormItemProps = {
12
+ ref: FieldRef;
13
+ name: NamePath;
14
+ initialValue?: any;
15
+ dependencies?: NamePath[];
16
+ };
17
+
18
+ export type UseFormItemResult = {
19
+ value: any;
20
+ errors: string[];
21
+ isTouched: boolean;
22
+ dependencyValues: any[];
23
+ onChange: (value: any) => void;
24
+ onErrorsChange: (errors: string[]) => void;
25
+ };
26
+
27
+ export default function useFormItem ({
28
+ ref,
29
+ name,
30
+ initialValue,
31
+ dependencies,
32
+ }: UseFormItemProps): UseFormItemResult {
33
+ const isFieldRegistered = useRef(false);
34
+ const { data, registerField, unregisterField, setFieldValue, setFieldError, getFieldError, isFieldsTouched } = useFormContext();
35
+
36
+ useDeepCompareEffect(() => {
37
+ const currentName = name;
38
+ const currentRef = ref;
39
+ registerField(currentName, currentRef, initialValue);
40
+ isFieldRegistered.current = true;
41
+
42
+ return () => {
43
+ unregisterField(currentName, currentRef);
44
+ isFieldRegistered.current = false;
45
+ };
46
+ }, [name]);
47
+
48
+ const value = useMemo(() => {
49
+ return get(data, name);
50
+ }, [data, name]);
51
+
52
+ const errors = isFieldRegistered.current ? getFieldError(name) : [];
53
+ const isTouched = isFieldRegistered.current ? isFieldsTouched([name]) : false;
54
+
55
+ const onChange = useCallback((value: any) => {
56
+ setFieldValue(name, value);
57
+ }, [name, setFieldValue]);
58
+
59
+ const onErrorsChange = useCallback((errors: string[]) => {
60
+ setFieldError(name, errors);
61
+ }, [name, setFieldError]);
62
+
63
+ const dependencyValues = useMemo(() => {
64
+ return (dependencies || []).map(dep => get(data, dep));
65
+ }, [data, dependencies]);
66
+
67
+ return {
68
+ value,
69
+ errors,
70
+ isTouched,
71
+ onChange,
72
+ onErrorsChange,
73
+ dependencyValues,
74
+ };
75
+ }
@@ -0,0 +1,36 @@
1
+ import { useCallback, useRef } from "react";
2
+
3
+ import { useUpdate } from "ahooks";
4
+
5
+ export default function useMap<
6
+ K extends string | number | symbol,
7
+ V,
8
+ > () {
9
+ const map = useRef(new Map<K, V>());
10
+
11
+ const update = useUpdate();
12
+
13
+ const set = useCallback((key: K, value: V) => {
14
+ map.current.set(key, value);
15
+ update();
16
+ }, [update]);
17
+
18
+ const setAll = useCallback((newMap: Iterable<readonly [K, V]>) => {
19
+ map.current = new Map(newMap);
20
+ update();
21
+ }, [update]);
22
+
23
+ const remove = useCallback((key: K) => {
24
+ map.current.delete(key);
25
+ update();
26
+ }, [update]);
27
+
28
+ const reset = useCallback(() => {
29
+ map.current.clear();
30
+ update();
31
+ }, [update]);
32
+
33
+ const get = useCallback((key: K) => map.current.get(key), []);
34
+
35
+ return [map.current, { set, setAll, remove, reset, get }] as const;
36
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,99 @@
1
+ import React, { forwardRef, useCallback, useImperativeHandle } from "react";
2
+
3
+ import FormItem from "./components/Item";
4
+ import FormKeep from "./components/Keep";
5
+ import FormLabel from "./components/Label";
6
+ import FormProvider from "./components/Provider";
7
+
8
+ import { filterUndefined } from "@/utils/tools";
9
+
10
+ import type { Field, FormContextProps, FormProviderConfiguration } from "./context/FormContext";
11
+
12
+ import { FormContextProvider, useFormContext } from "./context/FormContext";
13
+
14
+ export type FormActions =
15
+ Pick<FormContextProps, "setFieldValue" | "getFieldValue" | "getFieldsValue" | "setFields" | "getFields" | "resetFields" | "setFieldError" | "getFieldError" | "validateFields" | "isFieldsTouched">
16
+ & {
17
+ submit: () => Promise<Record<string, any> | undefined>;
18
+ reset: () => void;
19
+ };
20
+
21
+ export type FormProps =
22
+ & FormProviderConfiguration
23
+ & {
24
+ // goes to FormProvider
25
+ initialValues?: Record<string, any>;
26
+ onFieldsChange?: (changedFields: Array<{ name: string[]; value: any }>, allFields: Array<{ name: string[]; value: any }>) => void;
27
+ onValuesChange?: (changedValues: Record<string, any>, allValues: Record<string, any>) => void;
28
+
29
+ // goes to InnerForm
30
+ omitNil?: boolean;
31
+ onFinish?: (values: Record<string, any>) => void;
32
+ onFinishFailed?: (errors: Pick<Field, "name" | "errors">[]) => void;
33
+ children: React.ReactNode;
34
+ };
35
+
36
+ const InnerForm = forwardRef<FormActions, FormProps>(({
37
+ omitNil = true,
38
+ onFinish,
39
+ onFinishFailed,
40
+ children,
41
+ }, ref) => {
42
+ const context = useFormContext();
43
+
44
+ const handleSubmit = useCallback(async () => {
45
+ const errors = await context.validateFields();
46
+ if (errors) {
47
+ onFinishFailed?.(errors);
48
+ return;
49
+ }
50
+ const values = omitNil ? filterUndefined(context.getFieldsValue(), true) : context.getFieldsValue();
51
+
52
+ try {
53
+ onFinish?.(values);
54
+ } catch (e) {
55
+ console.log(e);
56
+ }
57
+
58
+ return values;
59
+ }, [context, omitNil, onFinish, onFinishFailed]);
60
+
61
+ useImperativeHandle(ref, () => ({
62
+ submit: handleSubmit,
63
+ reset: context.resetFields,
64
+ setFieldValue: context.setFieldValue,
65
+ getFieldValue: context.getFieldValue,
66
+ getFieldsValue: context.getFieldsValue,
67
+ setFields: context.setFields,
68
+ getFields: context.getFields,
69
+ resetFields: context.resetFields,
70
+ setFieldError: context.setFieldError,
71
+ getFieldError: context.getFieldError,
72
+ validateFields: context.validateFields,
73
+ isFieldsTouched: context.isFieldsTouched,
74
+ }), [context.getFieldError, context.getFieldValue, context.getFields, context.getFieldsValue, context.isFieldsTouched, context.resetFields, context.setFieldError, context.setFieldValue, context.setFields, context.validateFields, handleSubmit]);
75
+
76
+ return children;
77
+ });
78
+
79
+ interface FormComponent extends React.ForwardRefExoticComponent<FormProps & React.RefAttributes<FormActions>> {
80
+ Item: typeof FormItem;
81
+ Provider: typeof FormProvider;
82
+ Label: typeof FormLabel;
83
+ Keep: typeof FormKeep;
84
+ }
85
+
86
+ const Form = forwardRef<FormActions, FormProps>((props, ref) => {
87
+ return (
88
+ <FormContextProvider {...props}>
89
+ <InnerForm ref={ref} {...props} />
90
+ </FormContextProvider>
91
+ );
92
+ }) as FormComponent;
93
+
94
+ Form.Item = FormItem;
95
+ Form.Provider = FormProvider;
96
+ Form.Label = FormLabel;
97
+ Form.Keep = FormKeep;
98
+
99
+ export default Form;