Я подумал, что это будет интересная интеграция, о которой задумывались по крайней мере несколько разработчиков. Находясь в Другом, мы прошли процесс выбора базовых пакетов, чтобы облегчить разработку общей схемы проверки на основе AJV.
ПРИМЕЧАНИЕ. В то время, примерно два года назад, казалось, что интеграция AJV с формой реакции-хука была заброшена, поэтому я сам пошел по собственному пути. Начиная с RHF 2.9.0, в резолверы добавлена поддержка AJV. Я продемонстрирую, как я это сделал, несмотря ни на что, поскольку его можно использовать для интеграции других библиотек проверки или даже вашей собственной пользовательской логики проверки. Я также продемонстрирую встроенный преобразователь, созданный командой RHF — в конце концов, они практически идентичны.
AJV — это отличная библиотека проверки JSON, которая широко загружается на npm. Это также один из старейших и лучший, который я смог найти на основе спецификации JSON Schema. Идея; необработанный JSON более гибок для определения схем проверки, чем что-то программное, такое как Joi или Yup. В конце концов, JSON можно хранить несколькими способами, и его легче отделить от кода. Это также клей, который скрепляет наши приложения!
Если вы когда-либо использовали react-hook-form, то вы уже знаете, насколько фантастична эта маленькая библиотека форм. Быстрый, эффективный и простой в использовании. Он ставит все галочки и не мешает вам, а также предотвращает необходимость повторного создания колеса, как я видел во многих проектах с разработчиками React. Создатели также приложили все усилия, чтобы максимально предотвратить перезагрузку компонентов.
Со всем этим шумом покончено, поехали!
Если хотите, скопируйте источник этой статьи здесь.
Я настроил простой проект React с помощью приложения create-react-app с одной базовой формой:
Вот источник form.js
. Довольно простой материал, если вы работали с React и react-hook-form:
import React from "react"; import { useForm } from "react-hook-form"; import { validateResolver } from "./lib/validator"; import "./scss/form.scss"; const Form = () => { const { register, handleSubmit, formState: { errors } } = useForm({ context: "contactForm", resolver: validateResolver }); const onSubmit = formData => { console.log(`You said: ${JSON.stringify(formData, null, 4)}`) }; return ( <div> <form onSubmit={handleSubmit(onSubmit)}> <div> <label> Prefix <select {...register("prefix")}> <option value="">Select One</option> <option value="Mr.">Mr.</option> <option value="Ms.">Ms.</option> <option value="Mrs.">Mrs.</option> </select> </label> {errors.prefix && <span className="validation">{errors.prefix.message}</span>} </div> <div> <label> First Name <input type="text" {...register("firstName")} /> </label> {errors.firstName && <span className="validation">{errors.firstName.message}</span>} </div> <div> <label> Last Name <input type="text" {...register("lastName")} /> </label> {errors.lastName && <span className="validation">{errors.lastName.message}</span>} </div> <div> <label> Email <input type="text" {...register("email")} /> </label> {errors.email && <span className="validation">{errors.email.message}</span>} </div> <div> <label> Title <input type="text" {...register("title")} /> </label> {errors.title && <span className="validation">{errors.title.message}</span>} </div> <div> <button type="submit">Submit</button> </div> </form> </div> ); }; export default Form;
Первое, что следует отметить; Я ссылаюсь на функцию под названием validateResolver
в импорте, и она используется в хуке useForm
вместе со свойством контекста. Здесь происходит вся магия.
Пользовательский модуль валидатора, который содержит validateResolver
, выглядит так:
import Ajv from "ajv"; import AjvFormats from "ajv-formats"; import AjvErrors from "ajv-errors"; //schemas import { contactForm } from "../schema/contact"; //configure AJV const ajv = new Ajv({ allErrors: true, strict: false, $data: true }); AjvFormats(ajv); AjvErrors(ajv); /** * Custom validation resolver - passed to react-hook-form */ const validateResolver = async (data, context) => { //add schema by name ajv.removeSchema(context); ajv.addSchema(contactForm, context); //pull by context prop in useForm() let validate = ajv.getSchema(context); // let { schema } = validate; //run validation and pull errors let isValid = await validate(data); let { errors } = validate; //hack/workaround for bogus, always-present "required" error let errCount = errors.filter(err => err.instancePath !== "").length; //let data through? if (isValid || errCount === 0) { return { values: data, errors: {} }; } //reduce errors to format form expects let errorsFormatted = errors.reduce((prev, current) => ({ ...prev, [current.instancePath.replace("/", "")]: current }), {}); //return expected format return { values: {}, errors: errorsFormatted }; }; export { validateResolver };
Я не буду вдаваться в подробности здесь, так как это довольно простая интеграция, которая соответствует документации по пользовательской проверке формы реакции-хука, которую можно найти здесь.
Параметры конфигурации конструктора Ajv
можно найти здесь. По сути, я настроил его, чтобы разрешить пользовательские сообщения об ошибках в схемах и несколько других настроек, которые мне нравятся.
Вот схема contactForm
, на которую ссылается импорт:
export const contactForm = { type: "object", properties: { prefix: { type: "string", minLength: 2, maxLength: 4, errorMessage: "Select a prefix", }, firstName: { type: "string", minLength: 1, maxLength: 50, errorMessage: "Between 1 and 50 characters.", }, lastName: { type: "string", minLength: 1, maxLength: 50, errorMessage: "Between 1 and 50 characters.", }, title: { type: "string", maxLength: 50, errorMessage: "Less than 50 characters.", nullable: true }, email: { type: "string", maxLength: 100, pattern: "^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$", errorMessage: "Invalid email address.", nullable: true } }, required: ["firstName, lastName", "email"], additionalProperties: false };
TL;DR — функция validateResolver
добавляет схему в Ajv
, выполняет проверку и либо возвращает переданные ей исходные данные, если они действительны, либо возвращает специально отформатированный объект, который содержит ошибку сообщения, которые ожидает react-hook-form.
На данный момент, вероятно, легко представить, как вы можете использовать свою собственную логику проверки или любую другую неподдерживаемую библиотеку проверки — довольно легко!
После того, как вы клонировали проект, запустите npm install
и npm start
, чтобы запустить его, вы должны увидеть, что проверка работает, если вы нажмете «Отправить», ничего не заполняя:
Стоит отметить, что если вы устанавливаете реквизит minLength
для поля, он по существу обязателен, а реквизит required
не нужен. Я включил его, чтобы проиллюстрировать, как справиться с проблемным ответом, когда вы используете required
внутри validateResolver
.
Когда вы проверите это, вы заметите, что фокус и выделение полей выполняются автоматически. Довольно гладко! Как только вы, наконец, получите действительную форму, вы должны увидеть следующий вывод в консоли:
Поскольку поле заголовка является необязательным, для него остается пустая строка. Поскольку у него нет свойства minLength
и его нет в массиве required
, вам нужно будет проверить, добавив в поле более 50 символов, после чего вы увидите ошибку проверки:
Вот и все! Конечно, есть бесконечные возможности расширения этого в соответствии с вашими потребностями, если вы используете такой пользовательский валидатор. Например, я хотел иметь возможность приводить значения, чего не делает AJV, из коробки, поскольку это не является частью спецификации JSON Schema.
Я не буду расширять этот пост подробностями, но вот беглый взгляд на то, как я справился с необходимостью приведения пустой строки (например, поля заголовка) к нулевому значению, что было более удобно для серверной части, при отправке данных. к API:
Проверка AJV с использованием встроенного преобразователя
Ранее в этом посте я упоминал, что с тех пор, как я попытался интегрировать эти две технологии, команда RHF добавила встроенную интеграцию. Я быстро просмотрел исходный код, и они, по сути, делают то же, что и я, и код в действии выглядит очень похоже.
import React from "react"; import { useForm } from "react-hook-form"; import { contactForm } from "./schema/contact"; import { ajvResolver } from "@hookform/resolvers/ajv"; import "./scss/form.scss"; const Form = () => { const { register, handleSubmit, formState: { errors } } = useForm({ resolver: ajvResolver(contactForm) }); const onSubmit = formData => { console.log(`You said: ${JSON.stringify(formData, null, 4)}`) }; return ( <div> <form onSubmit={handleSubmit(onSubmit)}> <div> <label> Prefix <select {...register("prefix")}> <option value="">Select One</option> <option value="Mr.">Mr.</option> <option value="Ms.">Ms.</option> <option value="Mrs.">Mrs.</option> </select> </label> {errors.prefix && <span className="validation">{errors.prefix.message}</span>} </div> <div> <label> First Name <input type="text" {...register("firstName")} /> </label> {errors.firstName && <span className="validation">{errors.firstName.message}</span>} </div> <div> <label> Last Name <input type="text" {...register("lastName")} /> </label> {errors.lastName && <span className="validation">{errors.lastName.message}</span>} </div> <div> <label> Email <input type="text" {...register("email")} /> </label> {errors.email && <span className="validation">{errors.email.message}</span>} </div> <div> <label> Title <input type="text" {...register("title")} /> </label> {errors.title && <span className="validation">{errors.title.message}</span>} </div> <div> <button type="submit">Submit</button> </div> </form> </div> ); }; export default Form;
Одно предостережение: мне не удалось проверить это без «фиктивной» ошибки, вызванной наличием свойства required
в схеме проверки JSON. После удаления он вел себя так, как ожидалось. Мне это показалось странным, поскольку оно использовалось в примере на странице @hookform/resolvers npm.