You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Hook Form结合Zod与Material UI未显示校验错误问题

解决Material UI + React Hook Form + Zod校验错误不显示的问题

问题根源分析

你的代码存在三个核心问题导致错误信息无法正常展示:

  1. Zod Schema逻辑错误:z.string().min(10).or(z.string().nonempty('required')) 的or逻辑完全不符合需求——只要输入满足「非空」或「长度≥10」其中一项就会通过校验,但你实际需要的是「必填且长度至少10」,or的用法颠倒了逻辑。
  2. Material UI TextField未绑定错误状态:没有将React Hook Form的错误信息传递给TextField组件,导致组件无法识别校验失败状态,也就不会显示错误样式和文本。
  3. 表单提交方式不规范:用Button的onClick触发提交,未利用HTML表单的默认onSubmit事件,可能导致校验流程未被正确触发。

分步修复方案

1. 修正Zod Schema逻辑

将or改为链式调用,先校验必填规则,再校验最小长度,确保错误信息按优先级触发:

// @/utils/validations/AddNewProductFormValidation.ts
import { z } from 'zod';

export const productInfoSchema = z.object({
  product_name: z.string()
    .nonempty('产品名称不能为空') // 优先校验必填
    .min(10, '产品名称长度不能少于10个字符'), // 再校验长度
});

export type productInfoSchemaType = z.infer<typeof productInfoSchema>;

2. 给TextField绑定错误状态

在Controller的render函数中,通过fieldState获取当前字段的错误信息,传递给TextField的error和helperText属性,让Material UI组件自动渲染错误样式:

<Controller
  control={control}
  name='product_name'
  render={({ field, fieldState }) => (
    <TextField
      {...field}
      inputRef={field.ref}
      label='Product Name'
      error={!!fieldState.error} // 有错误时显示红色边框
      helperText={fieldState.error?.message} // 显示Zod定义的错误文本
      fullWidth // 可选:让输入框占满容器宽度
    />
  )}
/>

3. 规范表单提交方式

将form的onSubmit事件绑定为handleSubmit(onSubmit),并将Button的type设为submit,遵循HTML表单的默认行为:

<form onSubmit={handleSubmit(onSubmit)}>
  {/* 表单元素 */}
  <div className='justify-end'>
    <Button type="submit" variant='contained'>
      Next
    </Button>
  </div>
</form>

完整修复后的代码

ProductInfoForm Page

'use client';
import React from 'react';
import { Controller, SubmitHandler, useForm } from 'react-hook-form';
import {
  productInfoSchema,
  productInfoSchemaType,
} from '@/utils/validations/AddNewProductFormValidation';
import { Button, TextField } from '@mui/material';
import { zodResolver } from '@hookform/resolvers/zod';

const ProductInfoStep = () => {
  const { handleSubmit, formState: { errors }, control } = useForm<productInfoSchemaType>({
    mode: 'all',
    reValidateMode: 'onChange',
    resolver: zodResolver(productInfoSchema),
  });

  const onSubmit: SubmitHandler<productInfoSchemaType> = (data) => {
    console.log(data);
  };

  return (
    <div className='p-5 flex flex-col gap-5'>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Controller
          control={control}
          name='product_name'
          render={({ field, fieldState }) => (
            <TextField
              {...field}
              inputRef={field.ref}
              label='Product Name'
              error={!!fieldState.error}
              helperText={fieldState.error?.message}
              fullWidth
            />
          )}
        />
        <div className='justify-end mt-4'>
          <Button type="submit" variant='contained'>
            Next
          </Button>
        </div>
        {/* 可选:额外显示独立的错误文本 */}
        <div className='mt-2 text-red-500'>
          {errors.product_name && <p>{errors.product_name.message}</p>}
        </div>
      </form>
    </div>
  );
};

export default ProductInfoStep;

Zod Schema

import { z } from 'zod';

export const productInfoSchema = z.object({
  product_name: z.string()
    .nonempty('产品名称不能为空')
    .min(10, '产品名称长度不能少于10个字符'),
});

export type productInfoSchemaType = z.infer<typeof productInfoSchema>;

额外说明

  • 使用fieldState.error比直接读取formState.errors.product_name更精准,因为Controller会自动管理当前字段的实时状态。
  • mode: 'all'会在输入变化、表单提交、输入框失去焦点时都触发校验,若需调整校验时机,可修改mode参数(如onSubmit仅在提交时校验)。

内容的提问来源于stack exchange,提问作者Faraz Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 07:27:40