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

如何实现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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 03:45:05