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

Мои fieldValues такие:

export const schema = z.object({
  urls: z
    .array(
      z.object({
        path: z.string().url(),
        main: z.boolean(),
        id: z.string(),
      })
    )
    .min(1)
    .max(3)
    .refine(
      (urls) => {
        const result = urls.some((url) => url.main);
        console.info("refine", result);
        return result;
      },
      {
        message: "at least there is a main site!",
      }
    ),
});

Существует пользовательская логика проверки, логика заключается в том, что urls должен иметь url, помеченный как основной сайт.

Но я обнаружил, что встроенные min() и max() работают хорошо, и хотя мои пользовательские правила проверки срабатывают, error.message не обновляется.

Форма.tsx

import { Controller, useFieldArray, useFormContext } from "react-hook-form";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import RemoveCircleTwoToneIcon from "@mui/icons-material/RemoveCircleTwoTone";
import { Button } from "@mui/material";
import RHFTextField from "./RHFTextField";
import RHFSwitch from "./RHFSwitch";

const Form = () => {
  const { control } = useFormContext<Schema>();

  const { append, fields, remove } = useFieldArray({
    control,
    name: "urls",
  });

  return (
    <Controller
      name = "urls"
      control = {control}
      render = {({ field, fieldState: { error } }) => {
        return (
          <Stack spacing = {1}>
            <Stack direction = {"row"} alignItems = {"center"} spacing = {1}>
              <Typography variant = {"h6"}>urls</Typography>
              <Typography variant = {"body2"} color = {"error"}>
                {error?.message}
              </Typography>
            </Stack>
            {fields.map((url, index) => (
              <Stack key = {url.id} direction = {"row"}>
                <div className = "pr-1 h-[38px] flex items-center">
                  <RemoveCircleTwoToneIcon
                    color = {"error"}
                    onClick = {() => {
                      remove(index);
                    }}
                  />
                </div>
                <RHFTextField name = {`urls.${index}.path`} className = "flex-1" />
                <RHFSwitch name = {`urls.${index}.main`} />
              </Stack>
            ))}
            <Button
              variant = "text"
              onClick = {() => {
                append({
                  path: "",
                  main: false,
                  id: window.crypto.randomUUID(),
                });
              }}
            >
              add url
            </Button>
          </Stack>
        );
      }}
    />
  );
};

export default Form;

полный код

https://codesandbox.io/p/sandbox/react-hook-form-1-wwqnkk?file=%2Fsrc%2Fschema.ts

Как протестировать?

  1. нажмите кнопку add url.

  1. нажмите кнопку «Переключить». В это время консоль печатает refine true, указывая, что проверка прошла, но error?.message не обновлена.

🤔 А знаете ли вы, что...
React Testing Library - это инструмент для тестирования компонентов React.


1
70
1

Ответ:

Решено

Вы можете использовать метод trigger для запуска проверки пути "urls" при изменении компонента RHFSwitch:

const RHFSwitch = memo(({ label, name, ...props }: Props) => {
  const { control, trigger } = useFormContext();

  return (
    <Controller
      name = {name}
      control = {control}
      render = {({
        field: { value, onChange, ...field },
        fieldState: { error },
      }) => (
        <Stack direction = {"row"} justifyContent = {"space-between"}>
          <Typography variant = "h6">{label}</Typography>
          <Switch
            checked = {value}
            onChange = {(e) => {
              onChange(e);
              trigger("urls");
            }}
            {...field}
            {...props}
          />
        </Stack>
      )}
    />
  );
});

Объяснение

Ошибки, обрабатываемые методом refine Зода, регистрируются в пути "urls" объекта errors, который вы можете найти внутри formState.

Проверка пути "urls" запускается только тогда, когда вы добавляете/удаляете элемент в массив полей.

Я не уверен, но думаю, что RHF делает это намеренно, чтобы избежать ненужного повторного рендеринга материала.


Интересные вопросы для изучения

Реакция: «.map не является функцией»Редактирование ввода в строке таблицы теряет фокус в React422 Ошибка необрабатываемого объекта при отправке данных формы на серверную часть FastAPI с использованием ReactJs и Fetch API во внешнем интерфейсеЛучшая практика асинхронного получения данных API из разных конечных точек с помощью React?ОпасноSetInnerHTML не работает с <script>Ошибка назначения типа в массиве объекта: машинописный текст, динамические формы с использованием пользовательского интерфейса Shadcn, форма перехвата React и ZODКак использовать handleSubmit формы реагирования со схемой Zod: ввод данных onSubmit как z.input вместо z.outputКак указать значение по умолчанию (но не ноль) в числовом типе в форме zod и response-hooks?Форма перехвата React не будет правильно удалять элементы из контекста провайдера во вложенном компонентеРендеринг пользовательского интерфейса в React JS