ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Refine useSelect Hook 完全指南:无头 Select 数据绑定、搜索、默认值与实时更新

Refine useSelect Hook 完全指南:无头 Select 数据绑定、搜索、默认值与实时更新 Refine useSelect Hook 完全指南无头 Select 数据绑定、搜索、默认值与实时更新【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refineuseSelect是 Refine 核心包refinedev/core提供的无头headless数据 Hook用于为任意select类组件绑定后端数据。本文以 useSelect 官方文档 为骨架结合 packages/core/src/hooks/useSelect/index.ts 源码与 index.spec.ts 测试系统讲解其全部配置属性、返回值、常见实战模式与底层实现原理。读完本文你将能够用useSelect快速搭建分类选择、远程搜索Autocomplete、服务端排序/筛选、默认值回填以及实时更新的下拉框并理解它内部如何调用useList、useMany与 TanStack Query。useSelect 是什么useSelect用于管理任意select类组件——无论是原生 HTMLselect标签、React Select 还是其他自定义选择器。由于它被设计为 headless因此只负责数据获取与选项生成UI 完全由你掌控。这个 Hook 底层通过useList来获取数据也就是说数据请求最终走dataProvider的getList方法排序sorters、筛选filters、分页pagination等参数会原样透传给getList所有查询由 TanStack Query 管理天然支持缓存、重试、加载态等能力。useSelect对应的 UI 库衍生版本Ant Design SelectAnt Design 用户— 文档 — 示例Material UI AutocompleteMaterial UI 用户— 文档Mantine SelectMantine 用户— 文档如果你在使用上述 UI 库可直接使用其封装版本获得开箱即用的组件useSelect本身则面向无头headless场景。快速上手最小可用示例最基础的用法只需传入resource然后从返回值中取options渲染即可import { useSelect } from refinedev/core; interface ICategory { id: number; title: string; } const Categories: React.FC () { const { options } useSelectICategory({ resource: categories, }); return ( label Select a category: select {options?.map((option) ( option key{option.value} value{option.value} {option.label} /option ))} /select /label ); };这正是文档中 基础用法实时预览 所演示的形态Hook 内部根据getList返回的记录数组按optionLabel默认title与optionValue默认id生成{ label, value }选项。测试 index.spec.ts 验证了这一行为默认情况下options由记录的title作为label、id作为value组装而成。Properties完整配置指南resource必填resource会经由useList作为参数传给dataProvider的getList方法通常对应 API 端点路径具体含义取决于你的getList实现useSelect({ resource: categories, });如果存在同名资源可以传identifier而不是资源name。它只作为资源的主匹配键数据提供器的方法仍然使用在Refine/组件中定义的资源name。可参考identifier相关文档 与 创建 data provider。optionLabel 与 optionValue用于自定义选项的value与label默认值分别为optionLabel title、optionValue iduseSelectICategory({ resource: products, optionLabel: name, optionValue: productId, });两个属性都支持 lodashget风格的嵌套路径访问const { options } useSelect({ resource: categories, optionLabel: nested.title, optionValue: nested.id, });也支持传入函数函数会收到每个item作为参数const { options } useSelect({ optionLabel: (item) ${item.firstName} ${item.lastName}, optionValue: (item) item.id, });源码层面index.ts 中的getOptionLabel/getOptionValue会判断类型字符串走lodash/get路径取值函数则直接调用。测试用例也覆盖了嵌套optionLabel: nested.title的场景index.spec.ts。searchField指定onSearch函数将按哪个字段搜索const { onSearch } useSelect({ searchField: name }); onSearch(John); // 按 name 字段、值为 John 搜索默认逻辑见 index.ts若optionLabel是字符串默认用optionLabel的值否则默认用title字段。// optionLabel 为字符串时 const { onSearch } useSelect({ optionLabel: name }); onSearch(John); // 按 name 字段搜索 // optionLabel 为函数时 const { onSearch } useSelect({ optionLabel: (item) ${item.id} - ${item.name}, }); onSearch(John); // 按 title 字段搜索sorters控制选项的展示顺序sorters会经useList传给getList用于向 API 发送排序参数useSelect({ sorters: [ { field: title, order: asc, }, ], });文档配套的 排序实时预览 演示了通过按钮在asc/desc之间切换、选项即时按标题重排的完整交互。排序结构遵循CrudSorting接口。filters通过筛选来控制显示哪些选项。filters同样会经useList传给getList作为筛选参数发送给 APIuseSelect({ filter: [ { field: isActive, operator: eq, value: true, }, ], });注意属性名是filters复数上文示例沿用了原文档的写法实际使用时请使用filters。筛选结构遵循CrudFilters接口。此外useSelect内部会把filters与onSearch产生的搜索条件合并后一起传给useList见 index.ts。defaultValue让某些选项默认被选中并额外向选项数组追加对应数据。当select数据量很大、需要分页时defaultValue可能不在当前可见选项中从而破坏select组件。为了避免这种情况Hook 会额外发起一次useMany查询把defaultValue对应的记录取回并追加到当前选项数组中。useSelect({ defaultValue: 1, // 或 [1, 2] });defaultValue既可以是单个值也可以是数组。源码中通过Array.isArray将其统一规整为数组index.ts并构造useMany查询index.ts。默认值实时预览 演示了defaultValue: 3时选项默认被选中的效果。selectedOptionsOrder控制selectedOptions即defaultValue对应的选项在最终options中的排序位置in-place默认值选项排在最底部默认行为selected-first默认值选项排在最顶部。useSelect({ defaultValue: 1, // 或 [1, 2] selectedOptionsOrder: selected-first, // in-place | selected-first });源码中通过uniqBy(..., value)对两份选项做合并去重再按该顺序拼接index.ts。这与 useMany 文档 的查询结果相关。debounce为onSearch函数增加防抖延迟单位毫秒避免每次按键都触发请求useSelect({ resource: categories, debounce: 500, });源码中debounce默认值为300毫秒index.ts内部使用 lodash 的debounce包装onSearchindex.ts并将用户传入的onSearch通过ref保存以避免闭包过期index.ts。queryOptions用于向 TanStack Query 的useQuery传递额外选项例如重试次数useSelect({ queryOptions: { retry: 3, }, });它作用于主列表查询getList其类型为MakeOptionalUseQueryOptionsGetListResponseTQueryFnData, TError, GetListResponseTData, queryKey | queryFnindex.ts即 queryKey 与 queryFn 由 Refine 内部接管其余 TanStack Query 配置均可透传。pagination分页参数会作为参数传给getList用于向 API 发送分页查询参数useSelect({ pagination: { currentPage: 2, }, });currentPage指定页码pageSize指定每页条数useSelect({ pagination: { pageSize: 20, }, });mode决定是否使用服务端分页取值为off、client或serveruseSelect({ pagination: { mode: off, }, });源码中useSelect向useList传分页时pageSize默认取10index.ts。defaultValueQueryOptions当设置了defaultValue时Hook 会调用useMany查询所选记录。通过该属性可以自定义这次查询的选项如果不传则复用queryOptions中的值index.tsconst { options } useSelect({ resource: categories, defaultValueQueryOptions: { onSuccess: (data) { console.log(triggers when on query return on success); }, }, });其类型为MakeOptionalUseQueryOptionsGetManyResponseTQueryFnData, TError, GetManyResponseTData, queryKey | queryFnindex.ts。onSearch用于对选项做远程搜索Autocomplete返回一个设置搜索值的函数const { options, onSearch } useSelectICategory({ resource: categories, onSearch: (value) [ { field: title, operator: contains, value, }, ], }); // 在输入框的 onChange 中调用 input onChange{(e) onSearch(e.target.value)} /文档配套的 onSearch 实时预览 展示了完整的搜索交互。实现要点如果不传自定义onSearchHook 会基于searchField自动生成{ field: searchField, operator: contains, value }筛选条件index.ts如果传入自定义onSearch其返回值会覆盖现有filtersindex.tsHTML 原生select不原生支持 Autocomplete如需该能力可配合 React Select 或 use-select 之类的库使用搜索条件结构遵循CrudFilters接口。metameta是一个特殊属性用于向 data provider 方法传递额外信息常见用途包括针对特定用例自定义 data provider 方法使用纯 JavaScript 对象JSON生成 GraphQL 查询。下面示例把headers放在meta中传给create/getList等方法useSelect({ meta: { headers: { x-meta-data: true }, }, }); const myDataProvider { //... getList: async ({ resource, pagination, sorters, filters, meta }) { const headers meta?.headers ?? {}; const url ${apiUrl}/${resource}; const { data, headers } await httpClient.get(${url}, { headers }); return { data }; }, //... };源码中meta会与useMeta解析出的全局 meta 合并combinedMeta并同时传给useMany与useListindex.ts。更详细的说明见 General Concepts 文档中的 meta 概念。dataProviderName当存在多个 data provider 时用它指定使用哪一个useSelect({ dataProviderName: second-data-provider, });默认值为defaultindex.ts。适合不同资源挂在不同数据源的场景。successNotification / errorNotification需要NotificationProvider才能生效。数据获取成功时useSelect可以调用NotificationProvider的open方法展示成功通知并可自定义其内容useSelect({ successNotification: (data, values, resource) { return { message: ${data.title} Successfully fetched., description: Success with no errors, type: success, }; }, });数据获取失败时同理可自定义错误通知useSelect({ errorNotification: (data, values, resource) { return { message: Something went wrong when getting ${data.id}, description: Error, type: error, }; }, });liveMode / onLiveEvent / liveParams需要LiveProvider才能生效。liveMode决定收到相关实时事件后是否自动更新数据auto自动更新manual需要手动处理。可用于在应用中实时更新并展示数据useSelect({ liveMode: auto, });onLiveEvent是订阅到新事件时的回调函数useSelect({ onLiveEvent: (event) { console.log(event); }, });liveParams用于向 liveProvider 的subscribe方法传递参数。值得注意的是文档「Realtime Updates」一节指出useSelect挂载时会调用liveProvider的subscribe方法并携带channel、resource等参数以便订阅实时更新。同时defaultValue对应的useMany查询被显式设置为liveMode: offindex.ts即默认值回填查询不参与实时订阅。overtimeOptions用于请求超时的加载指示。interval为毫秒级时间间隔onInterval为每个间隔触发的回调。Hook 返回overtime对象elapsedTime为已耗时毫秒请求完成后变为undefinedconst { overtime } useSelect({ //... overtimeOptions: { interval: 1000, onInterval(elapsedInterval) { console.log(elapsedInterval); }, }, }); console.log(overtime.elapsedTime); // undefined, 1000, 2000, 3000 4000, ... // 用法示例 { elapsedTime 4000 divthis takes a bit longer than expected/div; }源码中通过useLoadingOvertime实现其isLoading同时监听主列表查询与默认值查询的isFetching状态index.ts。返回值useSelect返回以下值属性说明类型options生成的可用选项{ label: string; value: string }[]query主列表查询结果QueryObserverResult{ data: TData; error: TError }defaultValueQuerydefaultValue对应记录的查询结果QueryObserverResult{ data: TData; error: TError }onSearch设置搜索值的函数(value: string) voidovertime超时加载信息{ elapsedTime?: number }返回类型定义见 index.ts。options是options与selectedOptions按value去重合并后的结果index.ts。类型参数类型参数说明类型默认值TQueryFnData查询函数返回的数据类型需继承BaseRecordBaseRecordBaseRecordTError自定义错误对象需继承HttpErrorHttpErrorHttpErrorTDataselect函数返回的数据类型需继承BaseRecord未指定时默认使用TQueryFnData的值BaseRecordTQueryFnData各接口定义见 interface-referencesBaseRecord、HttpError。常见问题FAQ如何不分页获取全部数据将pagination.mode设为offuseSelect({ pagination: { mode: off, }, });注意data provider 必须实现对该模式的支持才能生效。如何为选项添加搜索Autocomplete使用onSearch它用于设置搜索值。简单示例如上文的 onSearch 一节所示实时预览 展示了输入框与下拉框联动的完整效果。如何确保defaultValue出现在选项中当手头只有id、但希望它在选择框中显示为已选中时Hook 会通过useMany发起请求取回数据并标记为已选中实时预览。如何修改选项的label与value使用optionLabel与optionValue默认值分别为optionLabeltitle、optionValueid。要改为name与categoryIduseSelect({ optionLabel: name, optionValue: categoryId, });可以手动创建选项吗当仅靠optionLabel与optionValue不够用时可以直接用query返回值手动构建const { query } useSelect({ resource: categories, }); const options query.data?.data.map((item) ({ label: item.name, value: item.id, })); return ( select {options?.map((option) ( option key{option.value} value{option.value} {option.label} /option ))} /select );源码实现剖析useSelect 内部如何工作理解底层实现有助于你更准确地使用它。核心实现位于 packages/core/src/hooks/useSelect/index.ts关键链路如下资源解析通过useResourceParams将传入的resource解析为{ resource, identifier }index.ts列表查询使用identifier默认值查询使用identifier ?? resource.name。meta 合并useMeta会把全局 meta 与传入的meta合并为combinedMetaindex.ts。两条数据链路主列表useList({ resource: identifier, sorters, filters: filters.concat(search), pagination, queryOptions, ... })index.tssearch即onSearch产生的筛选条件默认值仅当defaultValue非空时启用useMany查询index.ts。选项组装两条查询的onSuccess分别把记录映射为{ label, value }存入options与selectedOptionsindex.ts最终按selectedOptionsOrder拼接并经uniqBy(value)去重index.ts。搜索防抖onSearch由 lodashdebounce包装默认 300msindex.ts。因此useSelect本质上是一个“把getList结果转成下拉选项 用getMany补充默认选中项”的组合型数据 Hook你完全可以把它当作useListuseMany的便捷封装来理解并在需要时通过query返回值直接访问底层查询状态。完整的可运行示例位于 examples/core-use-select与 文档中的 CodeSandbox 示例 对应可用于本地验证上述全部配置。相关阅读useList Hook —useSelect的底层数据来源useMany Hook —defaultValue回填查询创建 data provider —getList/getMany的实现约定Live / Realtime —liveMode与onLiveEvent详解Notification Provider — 成功/失败通知配置General Conceptsmeta 概念 —meta的完整语义接口引用 —BaseRecord、HttpError、CrudSorting、CrudFilters等类型定义Ant Design useSelect / Material UI useAutoComplete / Mantine useSelect — UI 库封装版本【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表