refactor(Form): centralize error management

This commit is contained in:
Ali BARIN
2024-12-17 17:32:31 +00:00
parent 3de18ab46f
commit ce4e4b4885
4 changed files with 59 additions and 28 deletions

View File

@@ -2,6 +2,7 @@ import * as React from 'react';
import { FormProvider, useForm, useWatch } from 'react-hook-form'; import { FormProvider, useForm, useWatch } from 'react-hook-form';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
import useFormatMessage from 'hooks/useFormatMessage';
const noop = () => null; const noop = () => null;
@@ -18,6 +19,8 @@ function Form(props) {
...formProps ...formProps
} = props; } = props;
const formatMessage = useFormatMessage();
const methods = useForm({ const methods = useForm({
defaultValues, defaultValues,
reValidateMode, reValidateMode,
@@ -25,6 +28,8 @@ function Form(props) {
mode, mode,
}); });
const { setError } = methods;
const form = useWatch({ control: methods.control }); const form = useWatch({ control: methods.control });
const prevDefaultValues = React.useRef(defaultValues); const prevDefaultValues = React.useRef(defaultValues);
@@ -44,12 +49,51 @@ function Form(props) {
} }
}, [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 ( return (
<FormProvider {...methods}> <FormProvider {...methods}>
<form <form
onSubmit={methods.handleSubmit((data, event) => onSubmit={methods.handleSubmit(async (data, event) => {
onSubmit?.(data, event, methods.setError), try {
)} return await onSubmit?.(data);
} catch (errors) {
handleErrors(errors);
}
})}
{...formProps} {...formProps}
> >
{render ? render(methods) : children} {render ? render(methods) : children}

View File

@@ -68,7 +68,7 @@ function InstallationForm() {
}); });
}; };
const handleSubmit = async ({ fullName, email, password }, e, setError) => { const handleSubmit = async ({ fullName, email, password }) => {
try { try {
await install({ await install({
fullName, fullName,
@@ -77,29 +77,8 @@ function InstallationForm() {
}); });
} catch (error) { } catch (error) {
const errors = error?.response?.data?.errors; const errors = error?.response?.data?.errors;
if (errors) {
const fieldNames = Object.keys(defaultValues);
Object.entries(errors).forEach(([fieldName, fieldErrors]) => {
if (fieldNames.includes(fieldName) && Array.isArray(fieldErrors)) {
setError(fieldName, {
type: 'fieldRequestError',
message: fieldErrors.join(', '),
});
}
});
}
const generalError = getGeneralErrorMessage({ throw errors;
error,
fallbackMessage: formatMessage('installationForm.error'),
});
if (generalError) {
setError('root.general', {
type: 'requestError',
message: generalError,
});
}
} }
}; };

View File

@@ -1,5 +1,13 @@
import * as React from 'react';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
export default function useFormatMessage() { export default function useFormatMessage() {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
return (id, values = {}) => formatMessage({ id }, values);
const customFormatMessage = React.useCallback(
(id, values = {}) => formatMessage({ id }, values),
[formatMessage],
);
return customFormatMessage;
} }

View File

@@ -130,6 +130,7 @@
"webhookUrlInfo.description": "You'll need to configure your application with this webhook URL.", "webhookUrlInfo.description": "You'll need to configure your application with this webhook URL.",
"webhookUrlInfo.helperText": "We've generated a custom webhook URL for you to send requests to. <link>Learn more about webhooks</link>.", "webhookUrlInfo.helperText": "We've generated a custom webhook URL for you to send requests to. <link>Learn more about webhooks</link>.",
"webhookUrlInfo.copy": "Copy", "webhookUrlInfo.copy": "Copy",
"form.genericError": "Something went wrong. Please try again.",
"installationForm.title": "Installation", "installationForm.title": "Installation",
"installationForm.fullNameFieldLabel": "Full name", "installationForm.fullNameFieldLabel": "Full name",
"installationForm.emailFieldLabel": "Email", "installationForm.emailFieldLabel": "Email",
@@ -141,7 +142,6 @@
"installationForm.passwordMinLength": "Password must be at least 6 characters long.", "installationForm.passwordMinLength": "Password must be at least 6 characters long.",
"installationForm.mandatoryInput": "{inputName} is required.", "installationForm.mandatoryInput": "{inputName} is required.",
"installationForm.success": "The admin account has been created, and thus, the installation has been completed. You can now log in <link>here</link>.", "installationForm.success": "The admin account has been created, and thus, the installation has been completed. You can now log in <link>here</link>.",
"installationForm.error": "Something went wrong. Please try again.",
"signupForm.title": "Sign up", "signupForm.title": "Sign up",
"signupForm.fullNameFieldLabel": "Full name", "signupForm.fullNameFieldLabel": "Full name",
"signupForm.emailFieldLabel": "Email", "signupForm.emailFieldLabel": "Email",