如何为MUI AutoComplete组件添加无限滚动功能?
实现MUI5 AutoComplete滚动加载分页数据的方案
下面是一套可直接复用的实现方案,解决滚动下拉列表底部加载下一页数据的需求:
核心逻辑
- 通过
useRef获取AutoComplete的下拉面板DOM节点,监听其滚动事件 - 滚动到接近底部时(预留小阈值),触发下一页数据请求
- 维护数据列表、当前页码、加载状态、是否还有更多数据这几个核心状态,避免重复请求和无效加载
- 新数据请求成功后合并到已有列表,加载过程中在列表底部显示状态提示
完整代码实现
import React, { useState, useRef, useEffect, useCallback } from 'react'; import Autocomplete from '@mui/material/Autocomplete'; import TextField from '@mui/material/TextField'; import CircularProgress from '@mui/material/CircularProgress'; import ListItem from '@mui/material/ListItem'; import Typography from '@mui/material/Typography'; // 替换成你实际的分页接口 const fetchPaginatedData = async (page = 1, searchQuery = '') => { // 模拟接口延迟和返回数据,实际开发中替换为真实API调用 await new Promise(resolve => setTimeout(resolve, 800)); const mockData = Array.from({ length: 10 }, (_, i) => ({ id: (page - 1) * 10 + i + 1, label: `${searchQuery || 'Item'} ${(page - 1) * 10 + i + 1}` })); // 模拟第3页之后没有更多数据,实际根据接口返回判断 return page > 3 ? [] : mockData; }; const PaginatedAutoComplete = () => { const [options, setOptions] = useState([]); const [page, setPage] = useState(1); const [isLoading, setIsLoading] = useState(false); const [hasMore, setHasMore] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const listboxRef = useRef(null); // 加载下一页数据 const loadNextPage = useCallback(async () => { if (isLoading || !hasMore) return; setIsLoading(true); try { const newData = await fetchPaginatedData(page, searchQuery); if (newData.length === 0) { setHasMore(false); } else { setOptions(prev => [...prev, ...newData]); setPage(prev => prev + 1); } } catch (error) { console.error('加载数据失败:', error); // 可根据需求添加前端错误提示 } finally { setIsLoading(false); } }, [page, isLoading, hasMore, searchQuery]); // 监听下拉面板滚动事件 useEffect(() => { const listbox = listboxRef.current; if (!listbox) return; const handleScroll = () => { // 滚动到距离底部20px时触发加载,可根据需求调整阈值 if (listbox.scrollTop + listbox.clientHeight >= listbox.scrollHeight - 20) { loadNextPage(); } }; listbox.addEventListener('scroll', handleScroll); return () => listbox.removeEventListener('scroll', handleScroll); }, [loadNextPage]); // 搜索词变化时重置数据和页码 useEffect(() => { setOptions([]); setPage(1); setHasMore(true); // 300ms防抖,避免输入时频繁触发请求 const timer = setTimeout(() => { loadNextPage(); }, 300); return () => clearTimeout(timer); }, [searchQuery]); // 自定义下拉项渲染,添加加载提示 const renderOption = (props, option) => { // 加载提示项(用特殊标识区分普通选项) if (option.isLoading) { return ( <ListItem {...props} disabled> <CircularProgress size={20} sx={{ mr: 1 }} /> <Typography>加载中...</Typography> </ListItem> ); } return ( <ListItem {...props}> <Typography>{option.label}</Typography> </ListItem> ); }; // 拼接加载提示项到选项列表 const displayOptions = hasMore && isLoading ? [...options, { isLoading: true }] : options; return ( <Autocomplete options={displayOptions} getOptionLabel={(option) => option.label || ''} renderOption={renderOption} onInputChange={(event, newInputValue) => { setSearchQuery(newInputValue); }} renderInput={(params) => ( <TextField {...params} label="分页搜索" variant="outlined" fullWidth /> )} // 给下拉面板设置ref和滚动容器样式 ListboxProps={{ ref: listboxRef, sx: { maxHeight: 300, overflowY: 'auto', }, }} /> ); }; export default PaginatedAutoComplete;
关键细节说明
- 滚动阈值:设置20px的提前加载阈值,避免用户滚动到底部才开始加载,提升流畅度
- 防抖处理:搜索词变化时加300ms防抖,减少不必要的API请求
- 状态控制:用
isLoading防止重复请求,hasMore控制是否继续加载,避免无意义调用API - 搜索重置:用户输入新关键词时,清空现有数据、重置页码,重新从第一页开始加载
- 加载提示:在列表底部添加加载动画,让用户明确知道正在加载更多数据
内容的提问来源于stack exchange,提问作者Arad
相关产品推荐
相关产品推荐

