使用react-infinite-scroller实现无限滚动:先报错后loadMore无响应
解决react-infinite-scroller的两个核心问题:属性未定义错误与loadMore不触发
我来帮你一步步搞定遇到的这两个问题,咱们先从第一个报错说起,再解决loadMore不触发的情况。
一、解决Uncaught (in promise) TypeError: Cannot read property 'companies' of undefined
这个错误本质上有两个常见原因,咱们逐一排查修复:
1. 函数上下文this丢失,无法访问组件state
你最初的代码里,fetchCompanies作为类方法直接传给loadMore时,没有绑定组件上下文,导致函数内部的this不是组件实例,自然读不到this.state.companies。
解决方法二选一:
- 在构造函数里绑定方法:
constructor(props) { super(props); this.state = { companies: [], page: 0, resetResult: false }; // 绑定上下文,确保函数内this指向组件实例 this.fetchCompanies = this.fetchCompanies.bind(this); }
- 用箭头函数定义方法,自动绑定上下文:
// 箭头函数自动绑定组件this fetchCompanies = (page) => { traineeship.getAll(page).then(response => { // ...你的业务逻辑 }); }
2. API返回结构异常,_embedded.companies不存在
另一个可能是traineeship.getAll(page)返回的response.data里没有_embedded属性,直接访问_embedded.companies就会报错。咱们可以加一层安全判断,避免程序崩溃:
fetchCompanies = (page) => { traineeship.getAll(page).then(response => { // 用可选链操作符安全访问嵌套属性,避免undefined报错 if (response.data?._embedded?.companies) { this.setState(prevState => ({ // 用prevState获取最新状态,避免setState异步导致的旧值问题 companies: [...prevState.companies, ...response.data._embedded.companies], page: prevState.page + 1 // 记得更新页码,否则每次都请求同一页 })); } else { console.warn("API返回结构不符合预期", response); } }).catch(err => { console.error("请求公司列表失败", err); // 别忘了捕获异步请求错误 }); }
二、解决loadMore完全不触发的问题
你修改后的代码里loadMore={() => this.fetchCompanies.bind(this)}是错误写法,还有几个关键配置没到位,咱们来修正:
1. 正确传递loadMore函数
loadMore需要接收一个可直接执行的函数,而且它会自动传递当前页码(基于pageStart),所以直接传绑定好上下文的方法即可:
<InfiniteScroll pageStart={0} loadMore={this.fetchCompanies} // 直接传绑定好的方法 hasMore={this.state.hasMore} // 不要写死true,根据数据加载状态动态更新 loader={<div className="loader" key={0}>Loading ...</div>} useWindow={false} style={{ height: '600px', overflowY: 'auto' }} // useWindow=false时必须设置容器高度和滚动属性 > {this.state.companies.map((company, key) => ( // 列表项渲染,key尽量用数据唯一ID而非索引 <div key={company.id || key}>{company.name}</div> ))} </InfiniteScroll>
2. 动态控制hasMore状态
hasMore不能固定为true,否则会一直触发加载。你需要根据API返回的数据判断是否还有更多内容,比如返回的列表为空时,把hasMore设为false:
// 初始化state时添加hasMore字段 this.state = { companies: [], page: 0, resetResult: false, hasMore: true }; // 在fetchCompanies里更新hasMore fetchCompanies = (page) => { // 已经没有更多数据时直接返回,避免重复请求 if (!this.state.hasMore) return; traineeship.getAll(page) .then(response => { if (response.data?._embedded?.companies) { const newCompanies = response.data._embedded.companies; this.setState(prevState => ({ companies: [...prevState.companies, ...newCompanies], page: prevState.page + 1, // 返回数据为空则标记无更多内容 hasMore: newCompanies.length > 0 })); } else { this.setState({ hasMore: false }); console.warn("没有更多数据了"); } }) .catch(err => { console.error("请求失败", err); this.setState({ hasMore: false }); }); }
3. useWindow={false}的必要配置
当设置useWindow={false}时,InfiniteScroll会监听自身容器的滚动事件,所以必须给容器设置固定高度和overflow-y: auto,否则容器不会出现滚动条,自然触发不了滚动加载。
最终完整代码示例
class Traineeship extends React.Component { constructor(props) { super(props); this.state = { companies: [], page: 0, resetResult: false, hasMore: true }; this.fetchCompanies = this.fetchCompanies.bind(this); } componentDidMount() { this.fetchCompanies(this.state.page); } fetchCompanies(page) { if (!this.state.hasMore) return; traineeship.getAll(page) .then(response => { if (response.data?._embedded?.companies) { const newCompanies = response.data._embedded.companies; this.setState(prevState => ({ companies: [...prevState.companies, ...newCompanies], page: prevState.page + 1, hasMore: newCompanies.length > 0 })); } else { this.setState({ hasMore: false }); console.warn("API返回无更多数据"); } }) .catch(err => { console.error("获取公司列表失败:", err); this.setState({ hasMore: false }); }); } render() { return ( <div className="traineeship-container"> <InfiniteScroll pageStart={0} loadMore={this.fetchCompanies} hasMore={this.state.hasMore} loader={<div className="loader" key={0}>Loading ...</div>} useWindow={false} style={{ height: '600px', overflowY: 'auto', padding: '10px' }} > {this.state.companies.map((company, key) => ( <div key={company.id || key} className="company-item"> <h3>{company.name}</h3> <p>{company.address}</p> </div> ))} </InfiniteScroll> </div> ); } }
额外注意事项
- 确保
traineeship.getAll(page)能正确接收page参数,并返回对应页码的数据; - 列表项的
key优先使用数据的唯一ID(比如company.id),避免用数组索引导致渲染异常; - 如果需要重置列表(比如搜索后),可以重置
companies、page和hasMore状态,再调用fetchCompanies(0)重新加载。
内容的提问来源于stack exchange,提问作者Mizlul
相关产品推荐
相关产品推荐

