如何实现Formik Field与Material-UI Autocomplete多选复选框并修复下拉框关闭问题
问题原因与修复方案
核心问题定位
- 渲染层直接调用
setFieldValue触发全局重渲染:你在组件主体(非副作用钩子)中写了判断逻辑直接修改Formik状态,每次组件渲染只要满足条件就会触发Formik全局更新,连带当前组件重渲染,Autocomplete组件状态被重置导致弹窗关闭。另外asyncOptions !== []是无效判断,数组为引用类型,两个不同数组实例比较永远返回true,这段逻辑会在每次渲染时都执行,高频触发状态更新。 Field组件的component属性传入了临时生成的函数:每次渲染时getComponent都会生成新的函数实例,Formik的Field会判定为新组件类型,直接销毁旧组件重新挂载,导致Autocomplete弹窗被强制关闭。
修复步骤
1. 将渲染层的状态更新逻辑移入useEffect
删除组件主体中的状态更新判断,替换为副作用钩子,避免渲染时频繁触发状态变更:
// 删除原组件主体内的这段代码 // if (field.value.length > 0 && asyncOptions !== []) { // let fullObjValues = asyncOptions.filter((option) => // field.value.includes(option.value) // ); // if (fullObjValues.length > 0) { // setFieldValue(name, fullObjValues); // } // } // 新增useEffect处理值同步 useEffect(() => { // 仅当异步选项加载完成、且当前字段值为id数组时才做同步 if (field.value?.length > 0 && asyncOptions.length > 0 && typeof field.value[0] !== 'object') { const fullObjValues = asyncOptions.filter((option) => field.value.includes(option.value) ); if (fullObjValues.length > 0) { setFieldValue(name, fullObjValues); } } }, [asyncOptions, field.value, name, setFieldValue])
2. 移除不必要的Field组件包裹
你已经通过useField拿到了字段状态和方法,无需再用Field组件包裹Autocomplete,直接渲染组件即可,避免重挂载问题,将原return部分修改为:
return ( <div className={styles.element_wrapper} style={{ marginBottom: marginBottom }} > <Autocomplete disableCloseOnSelect multiple options={options ?? asyncOptions} value={field.value ?? []} limitTags={2} getOptionLabel={getOptionLabelCustom} onChange={handleChange} renderOption={(option, { selected }) => { return ( <React.Fragment> <Checkbox icon={icon} checkedIcon={checkedIcon} style={{ marginRight: 8 }} checked={selected} /> {option.name} </React.Fragment> ); }} renderInput={(params) => ( <TextField {...params} label={label} classes={classes} fullWidth variant="outlined" error={field.touched && Boolean(field.error)} helperText={field.touched && field.error} /> )} /> <div className={styles.error}> <ErrorMessage name={name} /> </div> </div> );
3. 可选:完善全选逻辑
你原有代码中声明的selectedOpt状态没有对应更新逻辑,如果需要实现全选功能,可在handleChange中添加对应判断:
const handleChange = (event: any, newValue: any) => { // 全选逻辑:选中All时返回所有选项,取消All时清空选择 const hasAll = newValue.some((item: any) => item.value === 'all'); const finalValue = hasAll ? asyncOptions : newValue; setFieldValue(name, finalValue); };
修改后disableCloseOnSelect属性会正常生效,选中选项后弹窗不会自动关闭,可连续多选。
内容的提问来源于stack exchange,提问作者Rohan Bari
相关产品推荐
相关产品推荐

