InfiniteLoader用户滚动时加载更多数据
WindowScroller将高度设置为视口高度,同步滚动条
AutoSizer虚拟长列表,将视口的数据转换为响应式数据,优化加载
<InfiniteLoader
isRowLoaded={this.isRowLoaded}
loadMoreRows={this.loadMoreRows}
rowCount={count}
>
{({ onRowsRendered, registerChild }) => (
<WindowScroller>
{({ height, isScrolling, scrollTop }) => (
<AutoSizer>
{({ width }) => (
<List
onRowsRendered={onRowsRendered}
ref={registerChild}
autoHeight // 设置高度为 WindowScroller 最终渲染的列表高度
width={width} // 视口的宽度
height={height} // 视口的高度
rowCount={count} // List列表项的行数
rowHeight={120} // 每一行的高度
rowRenderer={this.renderHouseList} // 渲染列表项中的每一行
isScrolling={isScrolling}
scrollTop={scrollTop}
/>
)}
</AutoSizer>
)}
</WindowScroller>
)}
</InfiniteLoader>
InfiniteLoader关键代码
// 判断列表中的每一行是否加载完成
isRowLoaded = ({ index }) => {
return !!this.state.list[index]
}
// 用来获取更多房屋列表数据
// 注意:该方法的返回值是一个 Promise 对象,并且,这个对象应该在数据加载完成时,来调用 resolve 让Promise对象的状态变为已完成。
loadMoreRows = ({ startIndex, stopIndex }) => {
return new Promise(resolve => {
API.get('/houses', {
params: {
cityId: this.value,
...this.filters,
start: startIndex,
end: stopIndex
}
}).then(res => {
// console.log('loadMoreRows:', res)
this.setState({
list: [...this.state.list, ...res.data.body.list]
})
// 数据加载完成时,调用 resolve 即可,更新state
resolve()
})
})
}
加载更多
// 判断 house 是否存在,不存在直接渲染组件会报错
// 如果不存在,就渲染 loading 元素占位
if (!house) {
return (
<div key={key} style={style}>
<p className={styles.loading} />
</div>
)
}
条件筛选栏吸顶效果,封装Sticky组件
<Sticky height={40}>
<Filter onFilter={this.onFilter} />
</Sticky>
具体代码,react操作dom
import React, { Component, createRef } from 'react'
import PropTypes from 'prop-types'
import styles from './index.module.css'
// dom.getBoundingClientRect() 获取元素的大小及其相对于视口的位置。
class Sticky extends Component {
// 创建ref对象
placeholder = createRef()
content = createRef()
// scroll 事件的事件处理程序
handleScroll = () => {
const { height } = this.props
// 获取DOM对象
const placeholderEl = this.placeholder.current
const contentEl = this.content.current
const { top } = placeholderEl.getBoundingClientRect()
if (top < 0) {
// 吸顶
contentEl.classList.add(styles.fixed)
placeholderEl.style.height = `${height}px`
} else {
// 取消吸顶
contentEl.classList.remove(styles.fixed)
placeholderEl.style.height = '0px'
}
}
// 监听 scroll 事件
componentDidMount() {
window.addEventListener('scroll', this.handleScroll)
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll)
}
render() {
return (
<div>
{/* 占位元素 */}
<div ref={this.placeholder} />
{/* 内容元素 */}
<div ref={this.content}>{this.props.children}</div>
</div>
)
}
}
Sticky.propTypes = {
height: PropTypes.number.isRequired
}
export default Sticky