-
Notifications
You must be signed in to change notification settings - Fork 220
/
submit.ts
executable file
·103 lines (85 loc) · 2.82 KB
/
submit.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import {useState, useCallback, useRef} from 'react';
import {useMountedRef} from '@shopify/react-hooks';
import {
FormMapping,
SubmitHandler,
SubmitResult,
FieldBag,
FormError,
FieldDictionary,
} from '../types';
import {mapObject, isField, propagateErrors, validateAll} from '../utilities';
export function useSubmit<T extends FieldBag>(
onSubmit: SubmitHandler<FormMapping<T, 'value'>> = noopSubmission,
fieldBag: T,
) {
const mounted = useMountedRef();
const [submitting, setSubmitting] = useState(false);
const [submitErrors, setSubmitErrors] = useState([] as FormError[]);
const fieldBagRef = useRef(fieldBag);
fieldBagRef.current = fieldBag;
const setErrors = useCallback((errors: FormError[]) => {
setSubmitErrors(errors);
propagateErrors(fieldBagRef.current, errors);
}, []);
const submit = useCallback(
async (event?: React.FormEvent) => {
const fields = fieldBagRef.current;
if (event && event.preventDefault && !event.defaultPrevented) {
event.preventDefault();
}
const clientErrors = validateAll(fields);
if (clientErrors.length > 0) {
setErrors(clientErrors);
return;
}
setSubmitting(true);
const result = await onSubmit(getValues(fields));
if (mounted.current === false) {
return;
}
setSubmitting(false);
if (result.status === 'fail') {
setErrors(result.errors);
} else {
setSubmitErrors([]);
}
},
[mounted, onSubmit, setErrors],
);
return {submit, submitting, errors: submitErrors, setErrors};
}
function getValues<T extends FieldBag>(fieldBag: T) {
return mapObject<FormMapping<T, 'value'>>(fieldBag, item => {
if (isField(item)) {
return item.value;
}
if (Array.isArray(item)) {
return item.map(valuesOfFields);
}
return valuesOfFields(item);
});
}
function valuesOfFields(fields: FieldDictionary<any>) {
return mapObject(fields, item => {
return item.value;
});
}
/**
* A convenience function for `onSubmit` callbacks returning values to `useSubmit` or `useForm`.
* @return Returns a `SubmitResult` representing your successful form submission.
*/
export function submitSuccess(): SubmitResult {
return {status: 'success'};
}
/**
* A convenience function for `onSubmit` callbacks returning values to `useSubmit` or `useForm`
* @param errors - An array of errors with the user's input. These can either include both a `field` and a `message`, in which case they will be passed down to a matching field, or just a `message`.
* @return Returns a `SubmitResult` representing your failed form submission.
*/
export function submitFail(errors: FormError[] = []): SubmitResult {
return {status: 'fail', errors};
}
function noopSubmission(_: unknown): Promise<SubmitResult> {
return Promise.resolve(submitSuccess());
}