Mui Autocomplete+react-hook-form防抖输入:输入框值回退问题修复
解决MUI Autocomplete + react-hook-form防抖查询时输入框值回退问题
问题根源
核心矛盾是Controller绑定的表单值与Autocomplete的输入框值耦合:当防抖触发远程查询引发组件重渲染时,Controller会将已选中的选项值同步回Autocomplete,直接覆盖用户正在输入的内容,打断输入流程。
解决方案:分离输入状态与表单绑定值
通过单独维护Autocomplete的输入框本地状态,将用户实时输入内容与react-hook-form的表单值解耦,仅在用户主动选中选项时同步表单值。
步骤1:重构FormAutocompleteField组件
import { useState, useCallback, useEffect } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; import Autocomplete from '@mui/material/Autocomplete'; import TextField from '@mui/material/TextField'; import debounce from 'lodash/debounce'; const FormAutocompleteField = ({ name, options, fetchOptions, label }) => { const { control } = useFormContext(); // 单独维护输入框实时值,不受表单值影响 const [inputValue, setInputValue] = useState(''); // 防抖触发远程查询 const debouncedFetch = useCallback( debounce((value) => { fetchOptions(value.trim()); // 传入防抖后的输入值触发查询 }, 500), [fetchOptions] ); // 处理输入变化:更新本地输入状态 + 触发防抖查询 const handleInputChange = (_, newInputValue) => { setInputValue(newInputValue); debouncedFetch(newInputValue); }; // 处理选项选中:同步选中值到表单,并更新输入框为选项文本 const handleOptionSelect = (_, newValue) => { field.onChange(newValue); setInputValue(newValue?.name || ''); }; // 表单初始值同步:若表单已有选中值,初始化输入框文本 useEffect(() => { if (field.value) { setInputValue(field.value.name || ''); } }, [field.value]); return ( <Controller name={name} control={control} render={({ field }) => ( <Autocomplete value={field.value} onChange={handleOptionSelect} inputValue={inputValue} onInputChange={handleInputChange} options={options} getOptionLabel={(option) => option.name} // 必须正确实现选项匹配逻辑,避免选中状态异常 isOptionEqualToValue={(option, value) => option.id === value.id} renderInput={(params) => ( <TextField {...params} label={label} variant="outlined" /> )} /> )} /> ); }; export default FormAutocompleteField;
步骤2:父组件中传递查询逻辑
在使用组件的父组件中,传入查询触发方法,并通过useQuery获取远程选项:
const [customerName, setCustomerName] = useState(''); const { data: customerOptions } = useQuery({ queryKey: ['customers', customerName], queryFn: () => fetch(`/api/customers?name=${customerName}`).then(res => res.json()), enabled: !!customerName, // 空输入时不触发查询 }); // 渲染组件 <FormAutocompleteField name="selectedCustomer" label="客户" fetchOptions={setCustomerName} options={customerOptions || []} />
关键注意事项
- 输入状态解耦:用本地
useState维护inputValue,彻底避免Controller的表单值覆盖用户实时输入。 - 防抖函数优化:用
useCallback包裹防抖函数,防止组件重渲染时创建新的防抖实例,导致防抖逻辑失效。 - 选项匹配逻辑:
isOptionEqualToValue必须精准实现(比如通过唯一ID匹配),确保Autocomplete能正确识别已选中选项。 - 表单重置同步:若需支持表单重置,需在重置时同步更新
inputValue,比如监听表单的reset事件。
内容的提问来源于stack exchange,提问作者Brendon Mendicino
相关产品推荐
相关产品推荐

