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

Material UI Autocomplete组件Bug求助:新增产品后显示异常

解决Autocomplete添加后显示异常的问题

核心问题分析

  1. value与options类型不匹配,匹配逻辑错误
    你的Autocomplete组件中,value传递的是p.name(字符串类型),但isOptionEqualToValue里却在比较option.name和value.name——如果options是字符串数组(从product.map生成的结果),那么option本身就是字符串,没有name属性;value是字符串,也没有name属性,这会导致该函数永远返回false,Autocomplete无法正确关联选中值和下拉选项,从而出现显示异常。

  2. 使用index作为key导致组件复用异常
    用数组index作为key,当添加新项时,React会复用之前的组件实例,导致Autocomplete的状态没有正确重置,引发显示错乱。

  3. product状态初始化与使用矛盾
    product初始化为[""](字符串数组),但后续却用option.name访问属性,这会导致初始options全部是"loading...",且类型不统一。

修复代码步骤

1. 修正Autocomplete的value匹配逻辑

如果product是从后端获取的对象数组(比如[{name: "产品1"}, {name: "产品2"}]),保持isOptionEqualToValue的同时,要确保value的结构和options元素一致;如果是字符串数组,直接比较字符串本身即可。

2. 替换index为唯一key

给每个productList项添加唯一id(比如时间戳或uuid),避免使用index作为key。

3. 修正product状态初始化

将product初始化为对象数组,和后端返回结构保持一致。

完整修复后的代码示例

import { useState } from 'react';
import { Autocomplete, TextField, CircularProgress } from '@mui/material';

const WaybillPage = () => {
  // 给每个产品项添加唯一id,避免用index当key
  const [productList, setProductList] = useState([{ id: Date.now(), name: "", quantity: 1 }]);
  // product初始化为对象数组,与后端返回结构一致
  const [product, setProduct] = useState([{ name: "" }]);
  const [loading, setLoading] = useState(false);
  // 假设使用react-hook-form,保留你的errors和register
  // const { register, formState: { errors } } = useForm();

  const handleQuantityChange = (e, index) => {
    const { name, value } = e.target;
    const list = [...productList];
    list[index][name] = value;
    setProductList(list);
  };

  const handleLabelChange = (name, index, newValue) => {
    const list = [...productList];
    // 根据newValue类型赋值,兼容对象或字符串
    list[index][name] = newValue?.name || newValue;
    setProductList(list);
  };

  const handleServiceAdd = () => {
    // 添加新项时生成唯一id
    setProductList([...productList, { id: Date.now(), name: "", quantity: 0 }]);
  };

  return (
    <div>
      {productList.map((p, index) => (
        <Autocomplete
          key={p.id} // 使用唯一id作为key
          // 当options是对象数组时的匹配逻辑
          isOptionEqualToValue={(option, value) => option.name === value.name}
          // 匹配选中值,无匹配时返回空对象
          value={product.find(item => item.name === p.name) || { name: "" }}
          disablePortal
          onChange={(e, newValue) => {
            handleLabelChange("name", index, newValue);
          }}
          id={`controllable-states-demo-${p.id}`} // 设置唯一id
          // 渲染选项,加载时显示loading
          options={loading ? [{ name: "loading..." }] : product}
          renderOption={(props, option) => (
            <li {...props}>{option.name}</li>
          )}
          renderInput={(params) => (
            <TextField
              // 每个字段使用唯一名称,避免表单注册冲突
              {...register(`productNameV-${p.id}`)}
              error={errors[`productNameV-${p.id}`] ? true : false}
              helperText={errors[`productNameV-${p.id}`]?.message}
              {...params}
              label={`Product ${index + 1}`}
              InputProps={{
                ...params.InputProps,
                endAdornment: (
                  <>
                    {loading ? <CircularProgress color="inherit" size={20} /> : null}
                    {params.InputProps.endAdornment}
                  </>
                ),
              }}
            />
          )}
        />
      ))}
      <button onClick={handleServiceAdd}>添加产品</button>
    </div>
  );
};

export default WaybillPage;

额外注意事项

  • 确保product状态更新时是正确的对象数组,加载完成后替换掉loading状态
  • 表单注册的字段名要唯一,避免多个TextField共用同一个字段名导致表单状态冲突
  • 如果你的options确实是字符串数组,可简化isOptionEqualToValue为(option, value) => option === value,并保持value为字符串类型

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 18:10:38