- 前端
- UI组件
【免费下载链接】rsuite
🧱 A suite of React components .
导读
本文聚焦 rsuite 组件库中InputPicker(带文本框输入的单选选择器)的异步搜索能力,讲解如何通过onSearch回调在用户输入关键字时动态拉取远程数据,并借助renderListbox与Loader呈现加载态。读完本文,你将掌握一套可直接落地的"输入即搜索"远程数据方案,并理解其底层数据流原理。
一、异步搜索的核心思路
InputPicker本身支持本地静态数据(data属性),但当数据量庞大或需要服务端过滤时,更常见的做法是不在本地预置全量数据,而是在每次搜索时请求远程接口,再把返回结果作为新的data重新喂给组件。
异步搜索的关键在于三个属性:
onSearch(search, event):输入框内容变化时触发,用于发起远程请求;data:受控地传入最新拉取到的选项列表;renderListbox(listbox):自定义列表内容的渲染,可在此插入加载动画。
从 InputPicker 源码 的类型定义可以看出,data是必填项(data *),onSearch与renderListbox均为可选回调,这为远程数据场景提供了完整的扩展入口。
二、完整示例:GitHub 用户远程搜索
原文档 async.md 给出的示例以 GitHub 用户搜索 API 作为数据源,是异步搜索最典型的落地形态:
import { InputPicker, HStack, Loader } from 'rsuite'; const useUsers = (defaultUsers = []) => { const [users, setUsers] = React.useState(defaultUsers); const [loading, setLoading] = React.useState(false); const featUsers = word => { setLoading(true); fetch(`https://api.github.com/search/users?q=${word}`) .then(response => response.json()) .then(data => { setUsers(data.items); setLoading(false); }) .catch(e => console.log('Oops, error', e)); }; return [users, loading, featUsers]; }; const App = () => { const [users, loading, featUsers] = useUsers(); return ( <InputPicker data={users} w={224} labelKey="login" valueKey="id" onSearch={featUsers} renderListbox={listbox => { if (loading) { return ( <HStack justifyContent="center"> <Loader content="Loading..." /> </HStack> ); } return listbox; }} /> ); }; ReactDOM.render(<App />, document.getElementById('root'));这段示例完整覆盖了异步搜索所需的全部要素,下面逐段拆解。
三、逐步拆解示例
1. 用自定义 Hook 封装请求逻辑
useUsers通过React.useState维护两份状态:
users:远程接口返回的选项数组,之后作为data传入组件;loading:布尔加载标记,用于切换列表的加载态。
featUsers函数接收搜索关键字word,先置loading = true,再通过fetch请求 GitHub 搜索接口,成功后用data.items更新users并复位loading。接口失败时仅打印错误,未复位loading,这是示例中简化处理的部分,生产环境建议在catch中也复位loading,避免加载态卡死。
2. labelKey 与 valueKey:字段映射
GitHub 接口返回的items数组中,每个用户对象的登录名是login,唯一 ID 是id。通过:
labelKey="login" valueKey="id"将"列表展示字段"映射到login、"选中值字段"映射到id。这与 InputPicker 默认约定 一致——默认labelKey = 'label'、valueKey = 'value',当远程数据字段名不同时,用这两个属性完成适配即可。
3. onSearch:每次输入触发远程请求
onSearch={featUsers}把请求函数直接绑定到搜索事件上。从源码看,输入框内容变化会经useSearch钩子处理:
- useSearch.ts 中的
handleSearch会先用关键字对当前data做本地过滤,再调用callback; - InputPicker.tsx 中的
onSearchCallback进一步完成"把过滤后首个选项设为焦点项"等逻辑,最后才触发用户传入的onSearch?.(searchKeyword, event)。
因此,onSearch的执行时机是每次输入关键字变化,且发生在组件完成内部搜索状态更新之后,与示例中的"输入即请求"行为完全吻合。
4. renderListbox:在加载期间替换列表内容
renderListbox={listbox => { if (loading) { return ( <HStack justifyContent="center"> <Loader content="Loading..." /> </HStack> ); } return listbox; }}renderListbox接收组件渲染好的listbox(选项列表节点)作为参数,允许返回任意自定义节点。在 InputPicker 源码 中,InputPickerPopup的渲染逻辑为:
{renderListbox ? renderListbox(listbox) : listbox}即一旦传入renderListbox,就以它的返回值为准。示例的做法是:加载中返回居中的Loader(HStack负责居中布局),加载完成后原样返回listbox。这样加载动画被无缝嵌入在弹层内,而不是整块替换弹层。
四、源码级原理:异步数据的受控流转
理解异步搜索,关键是搞清data在组件内部的流转。相关实现位于 useData.ts:
const data = useMemo(() => { return ([] as Option[]).concat(uncontrolledData, newData); }, [newData, uncontrolledData]); const dataWithCache = useMemo(() => { return ([] as Option[]).concat(data, cacheData); }, [data, cacheData]);- 传入的
controlledData(即data属性)变化时,useEffect会检测到新旧数据不同,更新内部状态并清空newData(useData.ts); - 因此异步场景下,只要在
onSearch里setUsers(远程结果),组件就能立即拿到新列表并重新渲染; - 内部同时维护
dataWithCache(data+cacheData的合并结果),这正是为异步搜索配套的cacheData属性的用武之地。
cacheData:异步搜索时保留已选值
异步搜索有一个常见痛点:用户已选中某个选项后,再次搜索时该选项因不在当前结果集而无法回显。cacheData属性专门解决这个问题,其类型定义在 InputPicker.tsx 中说明为"Option to cache value when searching asynchronously"。
用法示例:
<InputPicker data={users} cacheData={[{ id: 1, login: 'octocat' }]} labelKey="login" valueKey="id" onSearch={featUsers} />已选中的值对应的选项对象放入cacheData后,会与当前data合并参与匹配与回显,避免选中值在异步刷新列表时"消失"。
五、加载态的另一种形态:loading 属性
除了用renderListbox在列表区域显示Loader,InputPicker还自带loading属性(默认false)。从源码看:
loading被透传给触发器PickerToggle(InputPicker.tsx),在选择器本体上呈现加载中的旋转图标;- 同时
loading会进入按键事件处理(InputPicker.tsx),加载期间禁用键盘导航等交互。
两种形态可叠加使用:loading负责选择器本体反馈,renderListbox负责列表区域反馈。示例中选择的是后者,更贴合"搜索结果加载"的语义。
六、实战注意事项
1. 请求竞态与防抖
示例直接用fetch请求,未做防抖与竞态处理。真实项目建议:
- 用
AbortController取消过期请求,或在请求返回后校验"当前关键字是否仍等于发起时的关键字",避免旧结果覆盖新结果; - 结合
lodash/debounce等工具对featUsers做 300ms 左右防抖,减少无效请求。
2. 与 searchBy 的配合
onSearch只负责"通知外部去请求",而组件内部过滤仍由默认的shouldDisplay逻辑完成。如果远程返回的数据结构特殊,或需要在本地预过滤,可通过searchBy(keyword, label, item)自定义过滤函数(见 useSearch.ts 对searchBy的调用)。
3. 与 creatable 的边界
creatable允许用户把输入内容直接创建为新选项(相关示意见 creatable.md)。异步搜索场景下,远程结果尚未返回时输入的关键字可能被误判为"可创建选项",如果业务不允许,应避免同时开启creatable,或通过shouldDisplayCreateOption精确控制创建项的展示条件。
4. 空结果与错误态
示例在loading之外没有处理空结果与错误态。组件本身在无匹配项时会渲染noResultsText(可通过locale定制),而网络错误的 UI 提示需要由业务层接管,建议在renderListbox中根据错误状态返回自定义提示节点。
七、总结
InputPicker的异步搜索方案由三个支柱构成:
| 支柱 | 属性 | 职责 |
|---|---|---|
| 请求触发 | onSearch | 输入变化时发起远程请求 |
| 数据回填 | data(受控)+cacheData | 将远程结果喂给组件并保留已选值 |
| 加载呈现 | renderListbox/loading | 在列表区域或选择器本体显示加载态 |
配合labelKey/valueKey完成字段映射,即可在几乎不改组件结构的前提下,把静态选择器升级为服务端驱动的远程搜索选择器。上述接口的完整定义可在 InputPicker.tsx 与组件索引文档 index.md 中进一步查阅。
- 前端
- UI组件
【免费下载链接】rsuite
🧱 A suite of React components .
相关推荐
rsuite CheckPicker 异步加载选项数据实战:用 renderListbox、onOpen 与 onSearch 实现动态下拉
rsuite CheckPicker 异步加载选项数据实战:用 renderListbox、onOpen 与 onSearch 实现动态下拉 导读 在真实业务中
前端UI组件Ant Design Mentions 异步加载数据:基于 onSearch 与 loading 的远程搜索完整实战指南
Ant Design Mentions 异步加载数据:基于 onSearch 与 loading 的远程搜索完整实战指南 在 Ant Design 中,Ment
前端UI组件设计系统rsuite CascadeTree 异步数据加载实战:基于 `getChildren` 的懒加载原理与完整示例
rsuite CascadeTree 异步数据加载实战:基于 getChildren 的懒加载原理与完整示例 导读 本文聚焦 rsuite 级联树组件 Casc
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考