Вопрос или проблема
У нас есть react-hook-form + zod + react-query.
Как мне типизировать значения с помощью TypeScript, чтобы поймать ошибку, если я не предоставлю необязательные значения по умолчанию?
https://codesandbox.io/p/sandbox/react-form-form-question-hwvq7y
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useQuery } from "@tanstack/react-query";
function App() {
const { data: formValuesFromApi } = useQuery({
queryKey: ["query key"],
queryFn: async () => {
await new Promise((resolve) => {
setTimeout(() => resolve("done"), 1000);
});
return {
name: "John",
};
},
});
const schema = z.object({
name: z.string(),
});
type FormValues = z.infer<typeof schema>;
const methods = useForm<FormValues>({
resolver: zodResolver(schema),
values: formValuesFromApi,
defaultValues: {
// name: "", // НЕТ ЗНАЧЕНИЯ ПО УМОЛЧАНИЮ
},
});
const onSubmit = (data: FormValues) => {
console.log(data);
};
const formValues = methods.getValues();
// ОШИБКА!
// TS считает, что тип formValues.name - это строка
const nameChars = formValues.name.split("");
console.log("nameChars", nameChars);
return (
<form onSubmit={methods.handleSubmit(onSubmit)}>
<input {...methods.register("name")} />
<input type="submit" />
</form>
);
}
export default App;
Я пытался погуглить, чтобы найти способ правильно типизировать такой сценарий.
Ответ или решение
Вопрос о типизации значений в TypeScript для использования с react-hook-form, zod и react-query, особенно в ситуации, когда вы не предоставляете значения по умолчанию, действительно важен. В данной статье мы постараемся разобрать проблему с точностью, представив необходимые шаги и соображения.
Проблема с типизацией без значений по умолчанию
Когда вы используете react-hook-form в сочетании с zod для валидации форм, важно точно определить типы данных, которые вы собираетесь передавать. Если вы не задали значения по умолчанию, TypeScript не сможет гарантировать, что переменные будут заполнены действительными данными, что приведет к ошибкам во время выполнения.
Ваша ситуация заключается в том, что вы добавляете схему валидации с помощью zod, но при этом не предоставляете значения по умолчанию, что может вскрыть ошибки в дальнейшем. В этом контексте, определение правильного типа для значений формы должно учитывать возможность отсутствия данных.
Решение
Чтобы TypeScript правильно типизировал ваши значения и ловил ошибки, связанные с отсутствующими данными, следует задать проверку на наличие значений при инициализации и использовать интерфейсы, которые могут отражать возможное отсутствие данных.
-
Изменение схемы валидации:
Убедитесь, что ваша схема zod правильно описывает, что полеname
является обязательным. Это важный шаг, чтобы избежать ситуации, когда поле может быть неопределенным.const schema = z.object({ name: z.string().min(1, 'Имя обязательно'), // Добавим мин. длину });
-
Использование метода
setValue
:
Вы можете использовать методsetValue
в react-hook-form, чтобы обновлять значения формы по мере получения данных из API. Это также поможет избежать ситуации, когда значения могут быть неопределенными.useEffect(() => { if (formValuesFromApi) { methods.reset(formValuesFromApi); // Установить значения формы } }, [formValuesFromApi]);
-
Определение типа
FormValues
:
Определите типы данных в соответствии с вашим API и учетом возможного отсутствия значений.type FormValues = { name: string; // Подразумеваем, что name всегда будет строкой };
-
Проверка значений формы:
При работе с текущими значениями формы, также стоит учитывать, что они могут быть неполными. Для этого можно добавить проверки на наличие значений, прежде чем вызывать методы.
Пример окончательного кода
Вот пример вашего компонента с учётом всех рекомендаций:
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useQuery } from "@tanstack/react-query";
import { useEffect } from "react";
function App() {
const { data: formValuesFromApi } = useQuery({
queryKey: ["query key"],
queryFn: async () => {
await new Promise((resolve) => {
setTimeout(() => resolve("done"), 1000);
});
return { name: "John" };
},
});
const schema = z.object({
name: z.string().nonempty("Имя обязательно"), // обязательно
});
type FormValues = z.infer<typeof schema>;
const methods = useForm<FormValues>({
resolver: zodResolver(schema),
});
useEffect(() => {
if (formValuesFromApi) {
methods.reset(formValuesFromApi); // Устанавливаем значения формы из API
}
}, [formValuesFromApi]);
const onSubmit = (data: FormValues) => {
console.log(data);
};
const formValues = methods.getValues();
// Проверка наличия значений
const nameChars = formValues.name ? formValues.name.split("") : [];
console.log("nameChars", nameChars);
return (
<form onSubmit={methods.handleSubmit(onSubmit)}>
<input {...methods.register("name")} />
<input type="submit" />
</form>
);
}
export default App;
Заключение
Работа с React, TypeScript, react-hook-form и zod требует внимательного подхода к типизации и обработке значений. Не предоставляя значения по умолчанию, вы рискуете получить ошибки, которые могут быть трудными для диагностики. Правильная схема валидации и использование методов управления состоянием формы могут существенно упростить этот процесс и обеспечить безопасность вашего приложения.