如何用 useAutoComplete 在 Refine Material UI 中实现远程搜索选择?
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
在 Refine 的 Material UI 集成包(@refinedev/mui)中,当某个资源(例如categories)的记录需要作为下拉选项、且输入框内容要触发对后端的搜索请求时,可以用useAutocompletehook 管理 Material UI 的<Autocomplete>组件。它内部基于useListhook 取数,通过 dataProvider 的getList方法把filters、pagination、sorters等参数发给 API,因此“输入即搜索”的远程过滤能力来自onSearch属性,而不是前端对已加载数据的本地过滤。本文以categories资源为例,给出从基础接入到远程搜索、再到与表单结合的完整路径。
准备条件
- 一个已接入 Refine 的 React 应用,安装了
@refinedev/mui,并在<Refine/>中配置了dataProvider(useAutocomplete依赖其中的getList方法取数); - 目标资源已在应用中可用,本文示例统一使用
resource: "categories",其记录形如:
interface ICategory { id: number; title: string; }第一步:接入 useAutocomplete 获取基础选项
最小用法是传入resource,然后把返回的autocompleteProps整体展开到<Autocomplete>上:
import { useAutocomplete } from "@refinedev/mui"; import { Autocomplete, TextField } from "@mui/material"; interface ICategory { id: number; title: string; } const PostCreate: React.FC = () => { const { autocompleteProps } = useAutocomplete<ICategory>({ resource: "categories", }); return ( <Autocomplete {...autocompleteProps} getOptionLabel={(item) => item.title} isOptionEqualToValue={(option, value) => value === undefined || option?.id?.toString() === (value?.id ?? value)?.toString() } placeholder="Select a category" renderInput={(params) => ( <TextField {...params} label="Category" margin="normal" variant="outlined" required /> )} /> ); };autocompleteProps中包含options、loading、onInputChange、filterOptions四个属性,直接展开即可。完整可运行项目可参考仓库中的 field-material-ui-use-autocomplete 示例,hook 的实现见 useAutocomplete 源码。
第二步:用 onSearch 实现远程搜索
这是标题任务的核心。给useAutocomplete传一个onSearch回调,它接收输入框的文本值,返回一组CrudFilters结构的过滤条件,条件会经useList传给dataProvider的getList,由后端执行过滤:
const { autocompleteProps } = useAutocomplete<ICategory>({ resource: "categories", onSearch: (value) => [ { field: "title", operator: "contains", value, }, ], });两点需要注意:
onSearch一旦被使用,会覆盖已有的filters属性,两者不要同时依赖;- 输入过程会频繁触发请求,可配合
debounce属性做防抖(单位毫秒):
useAutocomplete({ resource: "categories", onSearch: (value) => [ { field: "title", operator: "contains", value }, ], debounce: 500, });从源码可以看到触发机制:autocompleteProps.onInputChange在输入事件类型为change时调用onSearch(value),类型为click(展开下拉)时调用onSearch("")。因此输入会带搜索词重新请求,点击展开则按空搜索词重新拉取。
可选分支:改为客户端过滤
如果数据量不大、不希望每次输入都请求后端,可以把onSearch显式传为undefined,禁用服务端过滤,改用 Material UI 的createFilterOptions在本地过滤:
import { createFilterOptions } from "@mui/material"; const { autocompleteProps } = useAutocomplete({ resource: "categories", }); const filterOptions = createFilterOptions({ matchFrom: "start", stringify: (option: any) => option.title, }); <Autocomplete {...autocompleteProps} getOptionLabel={(item) => item.title} onInputChange={(event, value) => {}} filterOptions={filterOptions} // 其余 props 同基础用法 />这是文档明确给出的替代路径:远程搜索与客户端过滤二选一,不要混在同一条操作链里。
与 useForm / CRUD 组件结合
在编辑已有记录时,表单里保存的往往是{ id: ... }对象。useAutocomplete支持与useForm(react-hook-form)结合,通过<Controller>把选中值写回表单字段:
import { Create, useAutocomplete } from "@refinedev/mui"; import { Box, Autocomplete, TextField } from "@mui/material"; import { useForm } from "@refinedev/react-hook-form"; import { Controller } from "react-hook-form"; const PostCreate: React.FC = () => { const { saveButtonProps, refineCore: { formLoading, query }, register, control, formState: { errors }, } = useForm<IPost, HttpError, IPost & { category: ICategory }>(); const { autocompleteProps } = useAutocomplete<ICategory>({ resource: "categories", }); return ( <Create isLoading={formLoading} saveButtonProps={saveButtonProps}> <Box component="form"> <Controller control={control} name="category" rules={{ required: "This field is required" }} render={({ field }) => ( <Autocomplete {...autocompleteProps} {...field} onChange={(_, value) => { field.onChange(value); }} getOptionLabel={({ title }) => title} isOptionEqualToValue={(option, value) => value === undefined || option?.id?.toString() === (value?.id ?? value)?.toString() } placeholder="Select a category" renderInput={(params) => ( <TextField {...params} label="Category" margin="normal" variant="outlined" error={!!errors.category} helperText={errors.category?.message} required /> )} /> )} /> </Box> </Create> ); };useAutocomplete也可以完全独立于useForm使用,上例只是文档给出的组合方式。
defaultValue:保证只有 id 时也能显示选中项
编辑页面经常只拿到id,而该记录可能不在当前分页拉到的选项里,<Autocomplete>会因此显示异常。传defaultValue后,hook 会额外发起一次useMany查询把该记录取回来并并入options:
useAutocomplete({ resource: "categories", defaultValue: 1, // 或 [1, 2] });注意:defaultValue不会设置默认选中,它只保证该值存在于选项列表中。要设置默认选中,应通过useForm的defaultValues或<Autocomplete>的value传入:
const form = useForm({ defaultValues: { category: { id: 1 }, // 默认选中值 }, }); const { autocompleteProps } = useAutocomplete({ resource: "categories", defaultValue: [1], // 确保默认值出现在选项中 });若希望已选记录显示在选项列表顶部而非底部,可用selectedOptionsOrder: "selected-first"(默认"in-place"排在底部)。
验证是否生效
- 选项是否来自后端:
autocompleteProps.loading为query.isFetching || defaultValueQuery.query.isFetching,请求发出到返回期间为true,说明数据确实经由getList拉取; - 搜索是否走远程:输入文本时观察网络请求,
getList请求应携带onSearch返回的过滤条件(如title contains ...);点击展开下拉时请求以空搜索词发起; - 搜索不生效时的排查入口:文档明确指出,默认行为是用
useList取数并把搜索条件作为参数发出,若结果不对,应检查自己 dataProvider 中getList函数如何处理filters参数——filters、sorters、pagination都只是被透传给getList,后端不识别这些参数时过滤不会生效。
其他常用参数(均直接透传给getList或作为查询选项):sorters控制选项排序(如{ field: "title", order: "asc" });pagination支持currentPage、pageSize和mode("off"/"client"/"server");queryOptions透传给底层useQuery(如{ retry: 3 });meta可向 dataProvider 方法传额外信息(如自定义请求头);多 dataProvider 场景用dataProviderName指定。
更多细节见文档 useAutocomplete,其中还包含实时更新(liveMode、onLiveEvent,需要配置LiveProvider)与successNotification/errorNotification(需要NotificationProvider)等可选能力。
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考