Синхронизация defaultValues в React Hook Form с react-query

    При разработке на React разработчики часто комбинируют react-hook-form и react-query. Возникает задача: как правильно обновлять поля формы, когда серверные данные меняются (например, при refetchOnWindowFocus или после успешного сохранения). Рассмотрим типовую реализацию и ответим на ключевые вопросы.

    Проблема: два сценария обновления defaultValues

    В вашем компоненте AgreementForm используется useForm с defaultValues, полученными из запроса react-query. Есть два сценария:

    • Фоновое обновление (refetchOnWindowFocus) - нужно частично синхронизировать поля, которые пользователь не трогал.
    • Успешный submit - нужно полностью сбросить форму до новых данных с сервера.

    Нужна ли синхронизация при фоновом обновлении?

    Да, в большинстве случаев синхронизация нужна. Если пользователь не редактировал поля, а серверные данные изменились (например, другой пользователь обновил договор), форма должна отображать актуальную информацию. Однако следует избегать сброса изменённых полей - это может вызвать путаницу. Используйте reset(defaultValues, { keepDirtyValues: true }).

    Реализация двух кейсов в одной форме

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

    const { reset, formState: { isSubmitting, isSubmitSuccessful } } = useForm({
      defaultValues: defaultValues ?? schema.getDefault(),
    });
    
    // Сценарий 1: фоновое обновление
    useEffect(() => {
      if (!defaultValues || isSubmitSuccessful) return;
      reset(defaultValues, {
        keepDirtyValues: true,
        keepErrors: true,
        keepTouched: true,
        keepIsSubmitted: true,
        keepSubmitCount: true,
        keepIsValid: true,
      });
    }, [reset, defaultValues, isSubmitSuccessful]);
    
    // Сценарий 2: после успешного submit
    useEffect(() => {
      if (!isSubmitSuccessful || !defaultValues) return;
      reset(defaultValues); // полный сброс
    }, [reset, defaultValues, isSubmitSuccessful]);

    Важные детали

    • Флаг isSubmitSuccessful помогает разделить логику: после submit сбрасываем всё, иначе - только нетронутые поля.
    • keepDirtyValues: true защищает изменённые пользователем поля от перезаписи при фоновом обновлении.
    • Не забудьте сбросить isSubmitSuccessful после обработки, чтобы избежать повторных срабатываний.

    Альтернативные подходы

    Можно использовать watch для отслеживания изменений или передавать key в компонент формы для полного пересоздания, но это менее гибко. Рекомендованный способ - через reset с конфигурацией.

    Часто задаваемые вопросы