Валидация только измененных полей

Yup по умолчанию ориентирован на валидацию целой схемы данных. Это означает, что вызов schema.validate(values) запускает проверку всех полей, даже если изменилось только одно значение. В реальных формах такой подход часто становится избыточным: пользователь редактирует одно поле, но получает ошибки сразу по всей форме, включая незатронутые значения.

В задачах интерфейсной валидации возникает необходимость ограничить проверку только изменёнными или затронутыми полями. Это снижает количество вычислений, уменьшает «шум» ошибок и делает поведение формы более предсказуемым.


Частичная валидация через validateAt

Механизм validateAt(path, value) позволяет проверять конкретное поле по пути в объекте данных.

import * as Yup from "yup";

const schema = Yup.object({
  email: Yup.string().email().required(),
  password: Yup.string().min(8).required(),
});

schema.validateAt("email", {
  email: "test@example.com",
  password: ""
});

Особенности подхода:

  • проверяется только указанный путь (email)
  • остальные поля игнорируются
  • используется полная схема, но ограниченная точкой доступа

Этот метод особенно полезен при обработке событий onChange или onBlur, когда изменяется только одно поле.


Использование touched/dirty состояния формы

В связке с форм-библиотеками, такими как Formik, часто используется механизм отслеживания изменённых полей (dirty и touched).

Formik предоставляет структуру состояния:

  • dirty — были ли изменения в форме
  • touched — какие поля были затронуты пользователем

Пример выборочной валидации:

const validateChangedFields = async (values, touched) => {
  const changedFields = Object.keys(touched);

  const errors = {};

  for (const field of changedFields) {
    try {
      await schema.validateAt(field, values);
    } catch (err) {
      errors[field] = err.message;
    }
  }

  return errors;
};

Такой подход позволяет:

  • избегать полной валидации формы
  • обновлять ошибки только для активных полей
  • уменьшать нагрузку при частых изменениях ввода

Интеграция с React Hook Form

В React Hook Form концепция dirty-логики встроена на уровне библиотеки. Она предоставляет объект formState.dirtyFields, содержащий только изменённые поля.

Пример использования с Yup:

import { useForm } from "react-hook-form";
import * as Yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";

const schema = Yup.object({
  username: Yup.string().required(),
  age: Yup.number().required(),
});

const { register, handleSubmit, formState } = useForm({
  resolver: yupResolver(schema),
});

Для частичной валидации можно использовать dirtyFields:

const onSub mit = async (data) => {
  const changed = Object.keys(formState.dirtyFields);

  for (const field of changed) {
    await schema.validateAt(field, data);
  }
};

Такой подход особенно эффективен в формах с большим количеством полей, где полная валидация на каждом изменении становится дорогой операцией.


Разбиение схемы через pick и reach

Yup позволяет создавать подмножества схем, что удобно для изолированной валидации.

pick

const partialSchema = schema.pick(["email"]);
await partialSchema.validate({ email: "test@example.com" });

Используется, когда набор изменяемых полей известен заранее.

reach

const emailSchema = Yup.reach(schema, "email");
await emailSchema.validate("test@example.com");

Подходит для динамического доступа к вложенным полям.


Валидация вложенных объектов

При работе со сложными структурами данных частичная валидация становится более тонкой задачей.

const schema = Yup.object({
  user: Yup.object({
    profile: Yup.object({
      name: Yup.string().required(),
      age: Yup.number().required(),
    })
  })
});

Валидация конкретного поля:

await schema.validateAt("user.profile.name", values);

Это позволяет:

  • не пересчитывать весь объект user
  • локализовать ошибки внутри вложенной структуры
  • уменьшить количество ненужных проверок

Ограничения частичной валидации

Несмотря на удобство, частичная проверка имеет ряд особенностей:

  • кросс-полевые зависимости могут нарушаться (например, confirmPassword зависит от password)
  • условные схемы (when) могут требовать контекста всей формы
  • сложные трансформации (transform) могут давать разные результаты при неполной проверке

Пример зависимости:

confirmPassword: Yup.string().oneOf([Yup.ref("password")])

При валидации только confirmPassword без password возможны некорректные результаты.


Комбинирование стратегии dirty-полей и validateAt

Наиболее стабильный подход строится на объединении нескольких механизмов:

  1. отслеживание изменённых полей (dirtyFields или touched)
  2. точечная валидация через validateAt
  3. хранение ошибок отдельно по ключам полей
const validateDirtyOnly = async (values, dirtyFields) => {
  const errors = {};

  for (const key of Object.keys(dirtyFields)) {
    try {
      await schema.validateAt(key, values);
    } catch (e) {
      errors[key] = e.message;
    }
  }

  return errors;
};

Такой подход даёт предсказуемую модель обновления ошибок без полной переработки всей схемы на каждом шаге ввода.


Поведение abortEarly и влияние на частичную проверку

Параметр abortEarly: false влияет только на сбор ошибок внутри одного поля или всей схемы, но не решает задачу ограничения области проверки.

await schema.validate(values, { abortEarly: false });

При частичной валидации через validateAt он практически не играет роли, так как проверяется один путь, а не вся структура.


Производственные сценарии применения

Частичная валидация становится критичной в следующих случаях:

  • формы с большим количеством полей (10+)
  • динамические формы (добавление/удаление блоков)
  • автосохранение при вводе
  • сложные анкеты с секциями

В таких сценариях переход от полной проверки к точечной снижает нагрузку и делает UX более стабильным за счёт отсутствия «перепроверки всего» при каждом изменении одного значения.