Skip to content

Commit

Permalink
Revert "[@mantine/form] use-form: Make functions referentially stable (
Browse files Browse the repository at this point in the history
…#1383)"

This reverts commit adb476c.
  • Loading branch information
rtivital committed May 8, 2022
1 parent e2fcb58 commit 900414a
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 178 deletions.
2 changes: 1 addition & 1 deletion docs/src/docs/form/use-form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ yarn add @mantine/form
- `schema` (optional) – [zod](https://www.npmjs.com/package/zod), [joi](https://www.npmjs.com/package/joi) or [yup](https://www.npmjs.com/package/yup) validation schema, if provided `validate` is ignored
- `initialErrors` (optional) – initial form errors, object of React nodes

Hook returns an object with the following properties (the functions are all referentially stable):
Hook returns an object with the following properties:

- `values` – current form values
- `setValues` – handler to set all form values
Expand Down

This file was deleted.

230 changes: 104 additions & 126 deletions src/mantine-form/src/use-form.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { useCallback } from 'react';
import { useState } from 'react';
import { formList, isFormList, FormList } from './form-list/form-list';
import { validateValues, validateFieldValue } from './validate-values/validate-values';
import { filterErrors } from './filter-errors/filter-errors';
import { getInputOnChange } from './get-input-on-change/get-input-on-change';
import { getErrorPath } from './get-error-path/get-error-path';
import { useStateRef } from './use-state-ref/use-state-ref';
import { usePropRef } from './use-prop-ref/use-prop-ref';
import type {
FormErrors,
FormRules,
Expand Down Expand Up @@ -71,48 +69,40 @@ export function useForm<T extends { [key: string]: any }>({
validate: rules,
schema,
}: UseFormInput<T>): UseFormReturnType<T> {
const [errors, setErrors, errorsRef] = useStateRef(filterErrors(initialErrors));
const [values, setValues, valuesRef] = useStateRef(initialValues);
const initialValuesRef = usePropRef(initialValues);
const rulesRef = usePropRef(rules);
const schemaRef = usePropRef(schema);
const [errors, setErrors] = useState(filterErrors(initialErrors));
const [values, setValues] = useState(initialValues);

const clearErrors = useCallback(() => setErrors({}), []);
const setFieldError = useCallback(
(field: keyof T, error: React.ReactNode) =>
setErrors((current) => ({ ...current, [field]: error })),
[]
);
const clearErrors = () => setErrors({});
const setFieldError = (field: keyof T, error: React.ReactNode) =>
setErrors((current) => ({ ...current, [field]: error }));

const clearFieldError = useCallback(
(field: keyof T) =>
setErrors((current) => {
const clone: any = { ...current };
delete clone[field];
return clone;
}),
[]
);
const clearFieldError = (field: keyof T) =>
setErrors((current) => {
const clone: any = { ...current };
delete clone[field];
return clone;
});

const setFieldValue = useCallback(<K extends keyof T, V extends T[K]>(field: K, value: V) => {
const setFieldValue = <K extends keyof T, V extends T[K]>(field: K, value: V) => {
setValues((currentValues) => ({ ...currentValues, [field]: value }));
clearFieldError(field);
}, []);
};

const setListItem = useCallback(
<K extends keyof T, V extends T[K]>(field: K, index: number, value: V[K][number]) => {
const list = valuesRef.current[field];
if (isFormList(list) && list[index] !== undefined) {
const cloned = [...list];
cloned[index] = value;
setFieldValue(field, formList(cloned) as any);
}
},
[]
);
const setListItem = <K extends keyof T, V extends T[K]>(
field: K,
index: number,
value: V[K][number]
) => {
const list = values[field];
if (isFormList(list) && list[index] !== undefined) {
const cloned = [...list];
cloned[index] = value;
setFieldValue(field, formList(cloned) as any);
}
};

const removeListItem = useCallback(<K extends keyof T>(field: K, indices: number[] | number) => {
const list = valuesRef.current[field];
const removeListItem = <K extends keyof T>(field: K, indices: number[] | number) => {
const list = values[field];

if (isFormList(list)) {
setFieldValue(
Expand All @@ -124,119 +114,107 @@ export function useForm<T extends { [key: string]: any }>({
) as any
);
}
}, []);
};

const addListItem = useCallback(
<K extends keyof T, V extends T[K]>(field: K, payload: V[number]) => {
const list = valuesRef.current[field];
const addListItem = <K extends keyof T, V extends T[K]>(field: K, payload: V[number]) => {
const list = values[field];

if (isFormList(list)) {
setFieldValue(field, formList([...list, payload]) as any);
}
},
[]
);
if (isFormList(list)) {
setFieldValue(field, formList([...list, payload]) as any);
}
};

const reorderListItem = useCallback(
<K extends keyof T>(field: K, { from, to }: { from: number; to: number }) => {
const list = valuesRef.current[field];
const reorderListItem = <K extends keyof T>(
field: K,
{ from, to }: { from: number; to: number }
) => {
const list = values[field];

if (isFormList(list) && list[from] !== undefined && list[to] !== undefined) {
const cloned = [...list];
const item = list[from];
if (isFormList(list) && list[from] !== undefined && list[to] !== undefined) {
const cloned = [...list];
const item = list[from];

cloned.splice(from, 1);
cloned.splice(to, 0, item);
setFieldValue(field, formList(cloned) as any);
}
},
[]
);
cloned.splice(from, 1);
cloned.splice(to, 0, item);
setFieldValue(field, formList(cloned) as any);
}
};

const validate = useCallback(() => {
const results = validateValues(schemaRef.current || rulesRef.current, valuesRef.current);
const validate = () => {
const results = validateValues(schema || rules, values);
setErrors(results.errors);
return results;
}, []);
};

const validateField = useCallback((field: string) => {
const results = validateFieldValue(
field,
schemaRef.current || rulesRef.current,
valuesRef.current
);
const validateField = (field: string) => {
const results = validateFieldValue(field, schema || rules, values);
results.hasError ? setFieldError(field, results.error) : clearFieldError(field);
return results;
}, []);
};

const onSubmit = useCallback(
const onSubmit =
(handleSubmit: (values: T, event: React.FormEvent) => void) => (event: React.FormEvent) => {
event.preventDefault();
const results = validate();
!results.hasErrors && handleSubmit(valuesRef.current, event);
},
[]
);
!results.hasErrors && handleSubmit(values, event);
};

const reset = useCallback(() => {
setValues(initialValuesRef.current);
const reset = () => {
setValues(initialValues);
clearErrors();
}, []);

const getInputProps = useCallback(
<K extends keyof T, U extends T[K], L extends GetInputPropsFieldType = 'input'>(
field: K,
{ type, withError = true }: { type?: L; withError?: boolean } = {}
): GetInputProps<L> => {
const value = valuesRef.current[field];
const onChange = getInputOnChange<U>((val: U) => setFieldValue(field, val)) as any;

const payload: any = type === 'checkbox' ? { checked: value, onChange } : { value, onChange };
};

if (withError && errorsRef.current[field as any]) {
payload.error = errorsRef.current[field as any];
}
const getInputProps = <
K extends keyof T,
U extends T[K],
L extends GetInputPropsFieldType = 'input'
>(
field: K,
{ type, withError = true }: { type?: L; withError?: boolean } = {}
): GetInputProps<L> => {
const value = values[field];
const onChange = getInputOnChange<U>((val: U) => setFieldValue(field, val)) as any;

return payload as any;
},
[]
);
const payload: any = type === 'checkbox' ? { checked: value, onChange } : { value, onChange };

const getListInputProps = useCallback(
<
K extends keyof T,
U extends T[K][number],
LK extends keyof U,
L extends GetInputPropsFieldType = 'input'
>(
field: K,
index: number,
listField: LK,
{ type, withError = true }: { type?: L; withError?: boolean } = {}
): GetInputProps<L> => {
const list = valuesRef.current[field];
if (withError && errors[field as any]) {
payload.error = errors[field as any];
}

if (isFormList(list) && list[index] && listField in list[index]) {
const listValue = list[index];
const value = listValue[listField];
const onChange = getInputOnChange<U[LK]>((val: U[LK]) =>
setListItem(field, index, { ...listValue, [listField]: val })
) as any;
const payload: any =
type === 'checkbox' ? { checked: value, onChange } : { value, onChange };
const error = errorsRef.current[getErrorPath([field, index, listField])];
return payload as any;
};

if (withError && error) {
payload.error = error;
}
const getListInputProps = <
K extends keyof T,
U extends T[K][number],
LK extends keyof U,
L extends GetInputPropsFieldType = 'input'
>(
field: K,
index: number,
listField: LK,
{ type, withError = true }: { type?: L; withError?: boolean } = {}
): GetInputProps<L> => {
const list = values[field];

if (isFormList(list) && list[index] && listField in list[index]) {
const listValue = list[index];
const value = listValue[listField];
const onChange = getInputOnChange<U[LK]>((val: U[LK]) =>
setListItem(field, index, { ...listValue, [listField]: val })
) as any;
const payload: any = type === 'checkbox' ? { checked: value, onChange } : { value, onChange };
const error = errors[getErrorPath([field, index, listField])];

return payload;
if (withError && error) {
payload.error = error;
}

return {} as any;
},
[]
);
return payload;
}

return {} as any;
};

return {
values,
Expand Down
11 changes: 0 additions & 11 deletions src/mantine-form/src/use-prop-ref/use-prop-ref.ts

This file was deleted.

14 changes: 0 additions & 14 deletions src/mantine-form/src/use-state-ref/use-state-ref.ts

This file was deleted.

0 comments on commit 900414a

Please sign in to comment.