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

MUI Autocomplete首次选择选项后不显示名称问题排查

Material UI Autocomplete组件首次选择选项显示异常问题解决

问题描述

使用Material UI的Autocomplete组件时,表单初始显示正常,点击展开选项列表也没问题,但首次选择选项时,contact.contactName值仅闪烁一下就消失,输入框标签会退到左上角(看起来像是已填充状态);再次点击同一选项时,contact.contactName值能正常显示。

现有代码

const [isContactListInitialised, setContactListInitialised] = useState(false);
const toggleContactListInitialised = () => { setContactListInitialised(state => !state) }

const [contactList, setContactList] = useState([]);

// ... 省略其他代码

const newJobStructure = {
    name: "",
    isJobStartLinked: true,
    isJobFinishLinked: true,
    outgoingDateTime: moment(),
    jobStartDateTime: moment(),
    returningDateTime: moment().add(1, "hours"),
    jobFinishDateTime: moment().add(1, "hours"),
    contactId: -1,
    jobTypeId: -1,
    deliveryLocationId: -1,
}

const [newJob, setNewJob] = useState(newJobStructure)
const handleNewJobChange = (event) => setNewJob({...newJob, [event.target.name]: event.target.value})

// ... 省略其他代码

useEffect(() => {
    if(!isContactListInitialised) { fetchAllContacts(); }
}, [])

// ... 省略其他代码

return(
    // ... 省略其他元素
    <Autocomplete
        id="contacts-group"
        options={contactList}
        getOptionLabel={(contact) => contact.contactName || ''}
        groupBy={(contact) => contact.firstLetter}
        value={newJob.contactId}
        onChange={(event, value) => setNewJob({...newJob, contactId: value.contactId})}
        freeSolo
        sx={{ width: "100%", minWidth: 400 }}
        renderInput={(params) => <TextField {...params} label="Contact Name" />} 
    />
    // ... 省略其他元素
)

问题原因

这是代码逻辑问题,不是MUI组件的Bug,核心原因是Autocomplete组件的value属性类型不匹配:

  • 组件要求value必须是options数组中的完整选项对象,或者与选项结构匹配的类型
  • 你当前设置value={newJob.contactId},传入的是数字类型的ID,而options是包含完整contact对象的数组,组件无法通过这个数字ID找到对应的选项对象,因此无法正确渲染选中项的标签文本。首次选择后组件短暂显示文本是因为onChange触发时临时拿到了选项对象,但后续state更新后,组件用数字ID找不到对应项,导致文本消失;第二次选择时可能因为组件内部缓存或状态同步,才勉强显示。

解决方案

调整value属性的取值,让它对应options中的完整contact对象,同时修改相关的state更新逻辑,有两种可行方案:

方案一:通过contactId匹配对应的选项对象(适合保持现有newJob结构)

  1. 修改Autocomplete的value属性,从contactList中找到对应ID的对象:
value={contactList.find(contact => contact.contactId === newJob.contactId) || null}
  1. 保持onChange逻辑不变,但注意当contactId为初始值-1时,find会返回undefined,所以用|| null兜底,避免组件报错。

方案二:直接在newJob中存储完整的contact对象(更简洁推荐)

  1. 修改newJobStructure,将contactId: -1替换为contact: null:
const newJobStructure = {
    // ... 其他属性保持不变
    contact: null,
    jobTypeId: -1,
    deliveryLocationId: -1,
}
  1. 更新Autocomplete的value和onChange逻辑:
<Autocomplete
    id="contacts-group"
    options={contactList}
    getOptionLabel={(contact) => contact.contactName || ''}
    groupBy={(contact) => contact.firstLetter}
    value={newJob.contact}
    onChange={(event, selectedContact) => setNewJob({...newJob, contact: selectedContact})}
    freeSolo
    sx={{ width: "100%", minWidth: 400 }}
    renderInput={(params) => <TextField {...params} label="Contact Name" />} 
/>
  1. 如果后续需要用到contactId,直接通过newJob.contact?.contactId获取即可。

额外优化点

你的useEffect依赖数组为空,且fetchAllContacts后没有更新isContactListInitialised状态,会导致组件挂载后可能重复调用接口(如果组件重新渲染的话),建议在fetchAllContacts成功后设置状态:

const fetchAllContacts = async () => {
    // ... 接口请求逻辑
    const data = await api.getContacts();
    setContactList(data);
    setContactListInitialised(true); // 请求成功后标记初始化完成
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 00:55:13