MUI Autocomplete 完全指南:从组合框到自由输入,React 下拉自动补全组件的用法与源码剖析
发布时间:2026/9/6 19:23:27 作者:尧图编辑部 阅读量:1,286

MUI Autocomplete 完全指南从组合框到自由输入React 下拉自动补全组件的用法与源码剖析【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Googles Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-uiMUIMaterial UI的Autocomplete组件是一个被建议面板增强的普通文本输入框用于在单行文本框中完成两类典型场景一是从预定义集合中选取值组合框模式二是允许任意输入但提供搜索建议自由输入模式。本文基于官方文档 autocomplete.md 完整覆盖选项结构、受控状态、freeSolo、分组、异步请求、自定义过滤与无头 hookuseAutocomplete等全部实战主题并结合 组件源码 与 Hook 实现 剖析其过滤逻辑、可访问性属性与默认行为读完即可在项目中复制可运行的完整配置并理解每个 prop 背后的实现机制。组件定位与两大使用场景官方文档开宗明义Autocomplete 是react-select与downshift这类包的改进版专门用于为单行文本框设定取值方式。两种场景决定了后续配置走向组合框Combo box取值必须来自预定义集合。例如地点字段必须填入合法的地点名称自由输入Free solo可以填入任意值但建议值能帮用户节省时间。例如搜索框提示相似或历史搜索。最简单的组合框示例见 ComboBox.tsx——核心只是options加一个renderInputimport TextField from mui/material/TextField; import Autocomplete from mui/material/Autocomplete; import top100Films from ./top100Films; export default function ComboBox() { return ( Autocomplete disablePortal options{top100Films} sx{{ width: 300 }} renderInput{(params) TextField {...params} labelMovie /} / ); }从源码结构看组件本体 Autocomplete.js 内部直接调用useAutocomplete见该文件第 9 行import useAutocomplete, { createFilterOptions } from ../useAutocomplete并负责 Popper 弹层、Chip 标签、清除/展开图标与各类 CSS 工具类expanded、focused、popupOpen等见useUtilityClasses函数。也就是说组件 无头 Hook Material 视觉层这也是官方推荐用 Hook 做深度定制的原因。选项结构Options structure默认情况下组件接受两种选项结构interface AutocompleteOption { label: string; } // or type AutocompleteOption string;对应示例const options [ { label: The Godfather, id: 1 }, { label: Pulp Fiction, id: 2 }, ]; // or const options [The Godfather, Pulp Fiction];但选项并不局限于以上两种通过getOptionLabel即可使用任意结构。两条重要的官方约束对象选项必须提供isOptionEqualToValue以确保正确的选中与高亮判断。默认实现是严格相等源码中可直接看到useAutocomplete.js 定义了defaultIsOptionEqualToValue (option, value) option value。选项存在重复 label 时必须用getOptionKey提取唯一 keyconst options [ { label: The Godfather, id: 1 }, { label: The Godfather, id: 2 }, ]; return Autocomplete options{options} getOptionKey{(option) option.id} /;在 源码 中getOptionProps返回的key正是getOptionKey?.(option) ?? getOptionLabel(option)——这解释了为何不传getOptionKey时重复 label 会导致 React key 冲突。受控状态Controlled states组件有两个可受控状态二者相互独立应分别控制value 状态value/onChange组合表示用户选定的值例如按Enter后inputValue 状态inputValue/onInputChange组合表示文本框中当前显示的内容。受控/非受控的定义组件由父组件用 props 管理时为受控由自身本地 state 管理时为非受控。官方示例 ControllableStates.tsx 完整演示了两个状态的分离const [value, setValue] React.useStatestring | null(options[0]); const [inputValue, setInputValue] React.useState(); Autocomplete value{value} onChange{(event, newValue) setValue(newValue)} inputValue{inputValue} onInputChange{(event, newInputValue) setInputValue(newInputValue)} idcontrollable-states-demo options{options} sx{{ width: 300 }} renderInput{(params) TextField {...params} labelControllable /} /引用稳定性警告原文档重点若你受控value必须保证它在渲染之间引用稳定——值本身不变时引用也不应变。// ⚠️ BAD return Autocomplete multiple value{allValues.filter((v) v.selected)} /; // GOOD const selectedValues React.useMemo( () allValues.filter((v) v.selected), [allValues], ); return Autocomplete multiple value{selectedValues} /;第一个示例中allValues.filter每次渲染都返回新数组会破坏组件内部的状态比较修复方式是 memoize使 value 仅在必要时变化。自由输入Free solo设置freeSolo后文本框可包含任意值。搜索输入freeSolo的主要设计目标是搜索框场景类似 Google 搜索。FreeSolo.tsx 展示了三种形态// 形态一纯字符串选项 自由输入 Autocomplete idfree-solo-demo freeSolo resetHighlightOnMouseLeave options{top100Films.map((option) option.title)} renderInput{(params) TextField {...params} labelfreeSolo /} / // 形态二禁用清除图标 原生 search 类型输入 Autocomplete freeSolo disableClearable options{top100Films.map((option) option.title)} renderInput{(params) ( TextField {...params} labelSearch input slotProps{{ ...params.slotProps, input: { ...params.slotProps.input, type: search }, }} / )} /类型不匹配警告使用 freeSolo 且选项不是字符串时需谨慎——用户键入产生的值始终是字符串与选项类型无关。示例中的形态三给出了标准解法getOptionLabel同时兼容字符串与对象isOptionEqualToValue判断字符串值时按option.title value比较Autocomplete options{top100Films} getOptionLabel{(option) (typeof option string ? option : option.title)} // value 可能是对象与 option 同型也可能是字符串如按 Enter 时 isOptionEqualToValue{(option, value) { if (typeof value string) { return option.title value; } return option.title value.title; }} /Creatable可创建模式若希望 freeSolo 呈现增强版 select的体验combo box like官方建议同时设置selectOnFocus帮助用户快速清空已选值clearOnBlur帮助用户输入新值handleHomeEndKeys让 Home/End 键在弹层内移动焦点resetHighlightOnMouseLeave指针离开弹层时清除鼠标创建的高亮在选项尾部追加一个占位选项如Add YOUR SEARCH。对应示例见 FreeSoloCreateOption.tsx另一种做法是用户要添加新值时弹出对话框见 FreeSoloCreateOptionDialog.tsx。分组选项Grouped用groupByprop 对选项分组。官方提示选项必须同时按分组维度排序否则会出现重复的组标题。这一点在源码中同样得到印证useAutocomplete.js 在开发环境下会对重复组头输出警告MUI: The options provided combined with the groupBy method of ${componentName} returns duplicated headers. You can solve the issue by sorting the options with the output of groupBy.分组示例见 Grouped.tsx。要自定义组渲染提供renderGroupprop它接收一个含两个字段的对象group— 表示组名的字符串children— 属于该组的列表项集合。RenderGroup.tsx 演示了如何用自定义标记与样式覆盖默认分组。禁用选项Disabled options单个选项可以禁用通过getOptionDisabled判定某选项不可选。示例见 DisabledOptions.tsx。源码中 getOptionProps 会将aria-disabled写入选项属性保证禁用状态对读屏器同样可见。useAutocomplete无头 Hook针对高级定制MUI 暴露了无头useAutocomplete()hook它接受与 Autocomplete 组件几乎相同的选项去掉所有渲染 JSX 相关的 props而 Autocomplete 组件正是构建在这个 Hook 之上import useAutocomplete from mui/material/useAutocomplete;Hook 返回的 API 在 useAutocomplete.js 中完整定义包括getRootProps、getInputLabelProps、getInputProps、getClearProps、getItemProps、getPopupIndicatorProps、getListboxProps、getOptionProps以及id、inputValue、value、dirty、expanded、popupOpen、focused、anchorEl、focusedItem、groupedOptions等状态量。示例基础用法见 UseAutocomplete.tsx完全自定义 UI 的示例见 CustomizedHook.tsx组件版本的定制可参考下文Customization章节的 GitHubLabel.tsx。异步请求组件支持两类异步用例打开时加载Load on open等待用户与组件交互后再加载选项期间显示加载进度状态。示例见 Asynchronous.tsx配合 server.ts 与数据文件 movies.ts 模拟网络延迟。边输入边搜索Search as you type若逻辑是每次击键都拉取新选项、由服务器用文本框当前值过滤建议对请求做节流。同时必须关闭内置过滤重写filterOptionsAutocomplete filterOptions{(x) x} /无限加载Infinite loadingInfiniteLoading.tsx 演示结合tanstack/react-query在滚动到列表末尾时增量拉取数据并用tanstack/react-virtual虚拟化列表。单值渲染与多值单值渲染Single value rendering默认multiple{false}时选中项以纯文本显示在输入框内。renderValue可自定义选中值的展示——适合加样式、附加信息或格式化。两条要点getItemProps提供data-item-index、disabled、tabIndex等 props应展开到渲染出的组件上以保证可访问性。源码中 getItemProps 实现 返回data-item-index、tabIndex: -1、onFocus与多值时的onDelete若自定义组件不是 MUI Chip需解构掉onDelete它是 Chip 专属 prop。示例见 CustomSingleValueRendering.tsx。多值Multiple valuesmultiple{true}时用户可选多个值这些items同样用renderValue定制要点与单值相同展开getItemProps的结果、按需解构onDelete。示例Tags.tsxChip 标签式多选FixedTags.tsx固定选项——通过禁用 chip 使某些标签不可删除CheckboxesTags.tsx用图标指示 listbox 中每个选项的选中状态LimitTags.tsxlimitTagsprop 限制未聚焦时显示的标签数量。尺寸Sizes想要更小的输入框用sizeprop如sizesmall见 Sizes.tsx。源码中根样式的tagSize${capitalize(size)}工具类Autocomplete.js 第 54 行正是按 size 生成的因此size同时影响标签样式与内边距。自定义Customization自定义输入框renderInput允许自定义渲染的输入框其参数包含必须转发的 props特别注意params.slotProps.input含其ref与params.slotProps.htmlInput。Autocomplete 通过params.slotProps.input.startAdornment渲染选中值。添加自定义前缀装饰时必须保留已提供的装饰const getInputSlotProps (params) ({ ...params.slotProps.input, startAdornment: ( {customStartAdornment} {params.slotProps.input.startAdornment} / ), }); Autocomplete options{options} renderInput{(params) ( TextField {...params} slotProps{{ ...params.slotProps, input: getInputSlotProps(params), }} / )} /;同理自定义endAdornment时也要保留params.slotProps.input.endAdornment其中包含 Autocomplete 内置控件清除图标、展开箭头。若在 Autocomplete 内使用自定义输入组件务必把 ref 转发到底层 DOM 元素。完整示例见 CustomInputAutocomplete.tsx。全局定制选项要全局定制应用中所有 Autocomplete 的选项渲染可用主题默认 props在defaultProps中设置renderOption。renderOption的第四个参数是ownerState含 props 与内部组件状态可通过其中的getOptionLabel显示 label。该做法能每个 Autocomplete 内容不同但选项样式一致见 GloballyCustomizedOptions.tsx。GitHub 标签选择器GitHubLabel.tsx 复刻了 GitHub 的 label picker圆点颜色 多选 tag是组合renderOption、renderValue、renderTags的综合范例。Hint提示信息AutocompleteHint.tsx 演示如何为 Autocomplete 增加 hint 功能。高亮HighlightsHighlights.tsx 依赖社区工具autosuggest-highlight约 1 kB对选项中的匹配文本做高亮。自定义过滤器Custom filter组件暴露了一个工厂createFilterOptions可创建用于filterOptionsprop 的过滤方法import { createFilterOptions } from mui/material/Autocomplete;createFilterOptions(config) filterOptionsconfig各参数均为可选参数类型 / 默认值说明ignoreAccentsbool默认true去除变音符号diacritics即忽略重音ignoreCasebool默认true全部转小写比较limitnumber默认null限制建议项数量。如100表示只显示前 100 个匹配项适合大量匹配且未配置虚拟化时matchFromany \| start默认any从任意位置或从开头匹配stringifyfunc控制选项如何转字符串以便与输入片段匹配trimbool默认false去除尾部空格返回的filterOptions可直接传给组件或 Hook 的同名参数。示例要求选项以查询前缀开头const filterOptions createFilterOptions({ matchFrom: start, stringify: (option) option.title, }); Autocomplete filterOptions{filterOptions} /;可运行的完整示例见 Filter.tsx。源码印证createFilterOptions 实现 与上表一一对应——stripDiacritics通过String.normalize(NFD)加正则去除变音符输入为空时直接返回全部选项匹配逻辑即matchFrom start ? candidate.startsWith(input) : candidate.includes(input)limit以slice(0, limit)截断。组件默认过滤器就是createFilterOptions()的无参产物第 62 行defaultFilterOptions。进阶模糊匹配需要更丰富的过滤机制如 fuzzy matching时官方推荐match-sorterimport { matchSorter } from match-sorter; const filterOptions (options, { inputValue }) matchSorter(options, inputValue); Autocomplete filterOptions{filterOptions} /;虚拟化VirtualizationVirtualize.tsx 演示在 10,000 个随机生成选项中搜索列表通过react-window实现虚拟化。配合上文createFilterOptions的limit或无限加载方案可覆盖超大选项集。事件拦截Events若要阻止组件默认的按键处理行为将事件的defaultMuiPrevented属性设为trueAutocomplete onKeyDown{(event) { if (event.key Enter) { // Prevents default Enter behavior. event.defaultMuiPrevented true; // your handler code } }} /从源码看getListboxProps中的onMouseDown/onScroll/onMouseLeave都会先检查event.defaultMuiPrevented再决定是否执行默认逻辑useAutocomplete.js 第 1471 行起因此该标记是组件约定的退出默认行为协议。已知限制Limitations浏览器 autocomplete / autofill浏览器会启发式地帮用户填充表单可能伤害组件体验。组件默认通过autoCompleteoff属性禁用输入框的自动补全记忆上次会话输入——源码中 getInputProps 明确设置了autoComplete: off、autoCapitalize: none、spellCheck: false以及role: combobox。但 Google Chrome 目前不支持该属性设置一个可能的变通是不传id让组件生成随机 id。对于浏览器可能提出的自动填充建议保存的登录、地址、支付信息若需规避可尝试给输入框取不泄露信息的名字例如idfield1而非idcountryid 留空时组件会使用随机 id设置autoCompletenew-password部分浏览器会对此属性给出强密码建议TextField {...params} slotProps{{ ...params.slotProps, htmlInput: { ...params.slotProps.htmlInput, autoComplete: new-password, }, }} /自定义 ListboxComponent若提供了自定义ListboxComponentprop必须确保预期的滚动容器上role属性为listbox否则滚动行为例如键盘导航时会不正确。源码中getListboxProps默认注入role: listbox与id: ${id}-listbox自定义组件时需自行保证这一点。可访问性AccessibilityAutocomplete 实现了 WAI-ARIA 组合框combobox作者规范官方鼓励为文本框使用 label。从源码可直接核对 ARIA 属性链输入框role: combobox配合aria-autocompleteautoCompleteprop 为真时取both否则list、aria-controls指向 listbox id、aria-expanded表示弹层状态getInputProps列表框role: listbox、aria-labelledby指向 label、多值时aria-multiselectablegetListboxProps每个选项role: option、aria-selected、aria-disabled、data-option-indexgetOptionProps。这正是上文单值/多值渲染强调必须展开getItemProps/getOptionProps返回值的原因——丢失这些属性会直接破坏键盘导航与读屏体验。小结与延伸阅读Autocomplete 的设计可以概括为三层无头 Hook 承载全部交互状态机过滤、高亮、键盘导航、ARIA 属性、组件层叠加 Material 视觉Popper 弹层、Chip 标签、图标与工具类、render*系列 prop 保留渲染自由度renderInput、renderOption、renderGroup、renderValue、renderTags。实践中最易踩的三个坑——对象选项缺isOptionEqualToValue、受控 value 引用不稳定、freeSolo 下字符串/对象类型混用——文档均已给出标准解法。进一步阅读建议按此路径核心文档docs/data/material/components/autocomplete/autocomplete.md组件源码packages/mui-material/src/Autocomplete/Autocomplete.jsHook 与过滤工厂packages/mui-material/src/useAutocomplete/useAutocomplete.js全部可运行示例ComboBox、FreeSolo、Asynchronous、InfiniteLoading、Virtualize、Tags 等 30 余个docs/data/material/components/autocomplete/。【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Googles Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考