Коли API на Laravel не пропускає дані, він відповідає 422 Unprocessable Content з JSON:
{
"message": "The email has already been taken. (and 1 more error)",
"errors": {
"email": ["Ця адреса вже зайнята."],
"items.0.qty": ["Кількість має бути не менше 1."]
}
}
Laravel повертає JSON (а не редирект назад), якщо запит має заголовок Accept: application/json - його обов'язково треба надсилати з fetch.
З React Hook Form - setError:
const { register, handleSubmit, setError, formState: { errors } } = useForm();
async function onSubmit(values) {
const response = await fetch('/api/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
body: JSON.stringify(values),
});
if (response.status === 422) {
const { errors: serverErrors } = await response.json();
for (const [field, messages] of Object.entries(serverErrors)) {
setError(field, { type: 'server', message: messages[0] }, { shouldFocus: true });
}
return;
}
if (!response.ok) {
setError('root.server', { message: 'Не вдалося зберегти. Спробуйте ще раз.' });
return;
}
}
{errors.root?.server && <p role="alert">{errors.root.server.message}</p>}
Що тут важливо:
- ключі Laravel з крапками (
items.0.qty) збігаються з іменами полів React Hook Form для масивів (register('items.0.qty')) - помилка потрапить точно до потрібного поля; - кілька повідомлень на поле - Laravel повертає масив; зазвичай показують перше;
- помилки, що не стосуються поля (сервер недоступний, 403, 500) - окремо, через
root.*; - серверні помилки скидаються при наступній зміні поля чи відправці - так поводиться React Hook Form для
setError.
Без бібліотеки - те саме зі станом: const [errors, setErrors] = useState({}) і useActionState, що повертає errors з 422-відповіді.
Inertia поводиться інакше: там Laravel відповідає не 422, а редиректом назад з помилками в сесії, і вони приходять у сторінку як проп errors (useForm().errors) - ручна обробка не потрібна.
Інші статуси, які варто обробити: 419 (сесія чи CSRF-токен застаріли - запропонувати оновити сторінку) і 429 (забагато спроб).