ra-core
Version:
Core components of react-admin, a frontend Framework for building admin applications on top of REST services, using ES6, React
102 lines • 4.19 kB
JavaScript
import { useCallback, useEffect, useMemo, useRef, } from 'react';
import { useForm, } from 'react-hook-form';
import merge from 'lodash/merge.js';
import { useRecordContext, useSaveContext } from "../controller/index.js";
import getFormInitialValues from "./getFormInitialValues.js";
import { getSimpleValidationResolver, } from "./validation/getSimpleValidationResolver.js";
import { setSubmissionErrors } from "./validation/setSubmissionErrors.js";
import { useNotifyIsFormInvalid } from "./validation/useNotifyIsFormInvalid.js";
import { sanitizeEmptyValues as sanitizeValues } from "./sanitizeEmptyValues.js";
import { useRecordFromLocation } from "./useRecordFromLocation.js";
/**
* Wrapper around react-hook-form's useForm
*
* This hook adds the following features to react-hook-form's useForm:
*
* - form initialization based on RecordContext
* - validation based on a validate function
* - sanitization of empty values
* - notification on invalid form
* - stop form submission event propagation
*/
export const useAugmentedForm = (props) => {
const { criteriaMode = 'firstError', defaultValues, formRootPathname, resolver, resetOptions, reValidateMode = 'onChange', onSubmit, sanitizeEmptyValues, validate, disableInvalidFormNotification, ...rest } = props;
const saveContext = useSaveContext();
const record = useRecordContext(props);
const defaultValuesIncludingRecord = useMemo(() => getFormInitialValues(defaultValues, record),
// eslint-disable-next-line
[
// eslint-disable-next-line
JSON.stringify({
defaultValues: typeof defaultValues === 'function'
? 'function'
: defaultValues,
record,
}),
]);
const finalResolver = resolver
? resolver
: validate
? getSimpleValidationResolver(validate)
: undefined;
const form = useForm({
criteriaMode,
defaultValues: defaultValuesIncludingRecord,
reValidateMode,
resolver: finalResolver,
...rest,
});
const formRef = useRef(form);
const { reset, formState } = form;
const { isReady } = formState;
const previousRecordId = useRef(record?.id);
useEffect(() => {
const recordIdChanged = record?.id !== previousRecordId.current;
previousRecordId.current = record?.id;
reset(defaultValuesIncludingRecord, recordIdChanged ? undefined : resetOptions);
}, [defaultValuesIncludingRecord, reset, resetOptions, record?.id]);
// notify on invalid form
useNotifyIsFormInvalid(form.control, !disableInvalidFormNotification);
const recordFromLocation = useRecordFromLocation();
const recordFromLocationApplied = useRef(false);
useEffect(() => {
if (!isReady)
return;
if (recordFromLocation && !recordFromLocationApplied.current) {
reset(merge({}, defaultValuesIncludingRecord, recordFromLocation), {
keepDefaultValues: true,
});
recordFromLocationApplied.current = true;
}
}, [defaultValuesIncludingRecord, recordFromLocation, reset, isReady]);
// submit callbacks
const handleSubmit = useCallback(async (values, event) => {
let errors;
const finalValues = sanitizeEmptyValues
? sanitizeValues(values, record)
: values;
if (onSubmit) {
errors = await onSubmit(finalValues, event);
}
if (onSubmit == null && saveContext?.save) {
errors = await saveContext.save(finalValues, event);
}
if (errors != null) {
setSubmissionErrors(errors, formRef.current.setError);
}
}, [onSubmit, saveContext, sanitizeEmptyValues, record]);
const formHandleSubmit = useCallback((event) => {
if (!event.defaultPrevented) {
// Prevent outer forms to receive the event
event.stopPropagation();
form.handleSubmit(handleSubmit)(event);
}
return;
}, [form, handleSubmit]);
return {
form,
handleSubmit,
formHandleSubmit,
};
};
//# sourceMappingURL=useAugmentedForm.js.map