如何将React Hook Form与React MUI Select组件结合并解决异常?
问题场景
使用 React Hook Form 搭配 MUI Select 组件时,出现初始值不生效、控制台抛出值越界及受控/非受控切换的错误,代码及问题如下:
原代码
import { zodResolver } from '@hookform/resolvers/zod'; import { Button, FormControl, FormHelperText, InputLabel, MenuItem, Select, } from '@mui/material'; import { type SubmitHandler, useForm } from 'react-hook-form'; import { z } from 'zod'; function App() { const formFieldsSchema = z.object({ selected: z.enum(['foo', 'bar', 'baz']), }); const { register, handleSubmit, formState: { errors }, } = useForm<z.infer<typeof formFieldsSchema>>({ resolver: zodResolver(formFieldsSchema), defaultValues: { selected: 'bar', }, }); const onSubmit: SubmitHandler<z.infer<typeof formFieldsSchema>> = (data) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <FormControl fullWidth error={errors.selected !== undefined}> <InputLabel>Selected</InputLabel> <Select {...register('selected')}> {['foo', 'bar', 'baz'].map((option) => ( <MenuItem key={option} value={option}> {option} </MenuItem> ))} </Select> <FormHelperText>{errors.selected?.message}</FormHelperText> </FormControl> <Button type="submit">Submit</Button> </form> ); }
预期与实际问题
- 预期行为:Select 默认选中
bar,切换选项时正常更新表单状态 - 实际错误:
- 初始渲染时抛出警告:
MUI: You have provided an out-of-range value
undefinedfor the select (name="selected") component. Consider providing a value that matches one of the available options or ''. The available values arefoo,bar,baz. - 切换选项时抛出错误:
A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.
- 初始渲染时抛出警告:
解决方案
核心原因是 MUI Select 作为受控组件,与 React Hook Form 的 register 方法适配性不佳,需使用 RHF 提供的 Controller 组件来统一管理第三方受控组件的状态同步。
修改步骤
- 从
react-hook-form导入Controller组件 - 用
Controller包裹 MUI Select,通过render属性传递组件所需的value和onChange,同时关联 InputLabel 的 ID 避免标签错位
修改后完整代码
import { zodResolver } from '@hookform/resolvers/zod'; import { Button, FormControl, FormHelperText, InputLabel, MenuItem, Select, } from '@mui/material'; import { type SubmitHandler, useForm, Controller } from 'react-hook-form'; // 新增导入Controller import { z } from 'zod'; function App() { const formFieldsSchema = z.object({ selected: z.enum(['foo', 'bar', 'baz']), }); const { control, // 新增control属性,用于Controller handleSubmit, formState: { errors }, } = useForm<z.infer<typeof formFieldsSchema>>({ resolver: zodResolver(formFieldsSchema), defaultValues: { selected: 'bar', }, }); const onSubmit: SubmitHandler<z.infer<typeof formFieldsSchema>> = (data) => console.log(data); return ( <form onSubmit={handleSubmit(onSubmit)}> <FormControl fullWidth error={errors.selected !== undefined}> <InputLabel id="select-label">Selected</InputLabel> {/* 添加id */} <Controller name="selected" control={control} render={({ field }) => ( <Select labelId="select-label" // 关联InputLabel的id label="Selected" {...field} // 传递value和onChange > {['foo', 'bar', 'baz'].map((option) => ( <MenuItem key={option} value={option}> {option} </MenuItem> ))} </Select> )} /> <FormHelperText>{errors.selected?.message}</FormHelperText> </FormControl> <Button type="submit">Submit</Button> </form> ); }
原理说明
Controller 是 React Hook Form 专门为第三方受控组件设计的适配层,它会自动同步表单状态与组件的 value,同时处理 onChange 事件的格式转换,确保初始默认值能正确传递给 MUI Select,避免出现 undefined 导致的受控/非受控切换错误。
内容的提问来源于stack exchange,提问作者baitendbidz

