Files
automatisch/packages/web/src/components/Form/index.jsx
2024-12-17 17:32:31 +00:00

117 lines
2.9 KiB
JavaScript

import * as React from 'react';
import { FormProvider, useForm, useWatch } from 'react-hook-form';
import PropTypes from 'prop-types';
import isEqual from 'lodash/isEqual';
import useFormatMessage from 'hooks/useFormatMessage';
const noop = () => null;
function Form(props) {
const {
children,
onSubmit = noop,
defaultValues,
resolver,
render,
mode = 'all',
reValidateMode = 'onBlur',
automaticValidation = true,
...formProps
} = props;
const formatMessage = useFormatMessage();
const methods = useForm({
defaultValues,
reValidateMode,
resolver,
mode,
});
const { setError } = methods;
const form = useWatch({ control: methods.control });
const prevDefaultValues = React.useRef(defaultValues);
/**
* For fields having `dependsOn` fields, we need to re-validate the form.
*/
React.useEffect(() => {
if (automaticValidation) {
methods.trigger();
}
}, [methods.trigger, form]);
React.useEffect(() => {
if (!isEqual(defaultValues, prevDefaultValues.current)) {
prevDefaultValues.current = defaultValues;
methods.reset(defaultValues);
}
}, [defaultValues]);
const handleErrors = React.useCallback(
function (errors) {
if (!errors) return;
let shouldSetGenericGeneralError = true;
const fieldNames = Object.keys(defaultValues);
Object.entries(errors).forEach(([fieldName, fieldErrors]) => {
if (fieldNames.includes(fieldName) && Array.isArray(fieldErrors)) {
shouldSetGenericGeneralError = false;
setError(fieldName, {
type: 'fieldRequestError',
message: fieldErrors.join(', '),
});
}
});
// in case of general errors
if (Array.isArray(errors.general)) {
for (const error of errors.general) {
shouldSetGenericGeneralError = false;
setError('root.general', { type: 'requestError', message: error });
}
}
if (shouldSetGenericGeneralError) {
setError('root.general', {
type: 'requestError',
message: formatMessage('form.genericError'),
});
}
},
[defaultValues, formatMessage, setError],
);
return (
<FormProvider {...methods}>
<form
onSubmit={methods.handleSubmit(async (data, event) => {
try {
return await onSubmit?.(data);
} catch (errors) {
handleErrors(errors);
}
})}
{...formProps}
>
{render ? render(methods) : children}
</form>
</FormProvider>
);
}
Form.propTypes = {
children: PropTypes.node,
defaultValues: PropTypes.object,
onSubmit: PropTypes.func,
render: PropTypes.func,
resolver: PropTypes.func,
mode: PropTypes.oneOf(['onChange', 'onBlur', 'onSubmit', 'onTouched', 'all']),
reValidateMode: PropTypes.oneOf(['onChange', 'onBlur', 'onSubmit']),
automaticValidation: PropTypes.bool,
};
export default Form;