如何让React Hook Form包裹的MUI Autocomplete在提交后重置输入值
问题根因
- 双状态管理冲突:同时用自定义
useState存表单值,又用RHF的Controller托管同一份数据,两套状态不同步。修改自定义state时,RHF内部维护的表单值没有更新,Autocomplete绑定的是RHF传来的值,自然不会响应自定义state的修改。 - Autocomplete的选中值由组件本身的
value属性控制,不是内部嵌套的TextField的value。之前给TextField绑定value={formValues}、defaultValue={formValues}的逻辑完全无效,管不住Autocomplete的选中项。 - 给Autocomplete加的
key={formValues}属于冗余逻辑:数组是引用类型,每次set新数组key都会变,触发组件重挂载,但重挂载时组件读取的是Controller设置的defaultValue,和实际选中的状态不匹配,反而会造成状态错乱。 - 提交时调用的
SetFormValues([])只修改了自定义state,没有同步更新RHF的内部表单状态,达不到清空选中项的效果。
正确实现逻辑
核心原则:完全把Autocomplete的状态托管给React Hook Form,删掉所有自定义的表单状态逻辑,用RHF内置的API完成值更新和重置
- 从
useForm中解构出reset、watch方法,删除所有自定义的formValues相关的useState声明。 - 在
useForm初始化时统一设置所有字段的默认值,避免分散赋值造成的不同步。 - Controller的render函数中,直接将RHF提供的
value绑定到Autocomplete的value属性上,不要从自定义state取值。 - Autocomplete的
onChange回调中,仅调用Controller提供的onChange方法将选中值同步给RHF,不需要额外操作自定义state。 - 提交成功的回调中,直接调用RHF的
reset方法,将对应字段重置为初始默认值,Autocomplete会自动响应值的变化清空选中态。 - 提交按钮的显示逻辑用RHF的
watch方法监听目标字段的状态判断,不要依赖自定义state。
修正后的可运行代码
const { control, handleSubmit, reset, watch } = useForm({ // 统一配置所有字段的初始默认值 defaultValues: { stock_list: [] } }); // 监听stock_list字段的实时值,用于控制提交按钮的显示 const watchedStockList = watch('stock_list'); const onSubmit = (data, e) => { axiosInstance .patch(`${URL}${slug}/`, { stock_list: data.stock_list.map(list => list.symbol), }) .then((res) => { getFinData(dispatch)(slug); // 提交成功后指定字段重置为空数组,Autocomplete自动清空选中内容 reset({ stock_list: [] }); }); }; return ( <Container component="main" maxWidth="md"> <div className={classes.container}> <Grid> <form noValidate onSubmit={handleSubmit(onSubmit)}> <Controller render={({ field: { onChange, value, name } }) => ( <Autocomplete className={classes.inputBar} id="stock_list" name={name} multiple options={options} ListboxComponent={ListboxComponent} renderGroup={renderGroup} filterOptions={filterOptions} filterSelectedOptions // 直接绑定RHF托管的当前值,不需要额外状态同步 value={value} // 仅将选中值同步给RHF,不需要额外操作自定义state onChange={(e, selectedValue) => onChange(selectedValue)} getOptionLabel={(option) => option.symbol} // 替换旧版废弃的getOptionSelected,新版MUI统一用isOptionEqualToValue判断选中态 isOptionEqualToValue={(option, value) => option.symbol === value.symbol} renderOption={(option) => ( <> <span style={{ fontWeight: 500, fontSize: "20px", paddingRight: "1rem" }}>{option.symbol}</span> <span style={{ color: "#C6C6C6", fontSize: "24px" }}> | </span> <span style={{ paddingLeft: "1rem" }}>{option.company}</span> </> )} renderInput={(params) => ( <Zoom in={tabSwitch === 0}> <TextField {...params} style={{ alignItems: 'center' }} name={name} variant="outlined" label="Companies" className={classes.inputBar} /> </Zoom> )} /> )} name="stock_list" control={control} defaultValue={[]} /> {watchedStockList && watchedStockList.length > 0 && ( <Button variant="contained" color="primary" type="submit" style={{display: "flex", alignItems: 'center', justifyContent: "center"}} > Add Stocks </Button> )} </form> </Grid> </div> </Container> );
额外注意事项
- 如果需要一次性重置表单的所有字段,直接调用
reset()不传参数即可,会自动读取useForm初始化时配置的defaultValues完成全局重置。 - 不要在Controller包裹的组件外部额外绑定自定义state来控值,Controller本身已经完成了值的双向绑定,额外的状态只会造成不同步问题。
内容的提问来源于stack exchange,提问作者yung peso
相关产品推荐
相关产品推荐

