UNPKG

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
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