React無(wú)限滾動(dòng)加載列表組件的封裝實(shí)現(xiàn)
前言
由于需要考慮后端接口的性能問(wèn)題,我們?cè)谡?qǐng)求業(yè)務(wù)數(shù)據(jù)列表的時(shí)候并不能直接請(qǐng)求全量數(shù)據(jù)。所以我們?cè)谡?qǐng)求數(shù)據(jù)時(shí)常見(jiàn)的方式是做分頁(yè)查詢。
對(duì)于前端交互而言,我們需要考慮如何優(yōu)雅的讓用戶觸發(fā)請(qǐng)求下一頁(yè)數(shù)據(jù)的接口。常用的方法有兩種:
- 提供顯示的分頁(yè)器,讓用戶自己手動(dòng)點(diǎn)擊下一頁(yè);
- 業(yè)務(wù)滾動(dòng)到某個(gè)閾值時(shí)自動(dòng)觸發(fā)下一頁(yè)請(qǐng)求;
對(duì)于移動(dòng)端,滾動(dòng)加載的交互是更加優(yōu)雅的處理方式。對(duì)于滾動(dòng)加載的能力,我們需要一個(gè)公共的組件來(lái)實(shí)現(xiàn)代碼的復(fù)用,避免每次都要為滾動(dòng)加載的需求傷腦筋。
效果圖
先看效果,增加信心
準(zhǔn)備工作
滾動(dòng)事件的參數(shù)中核心屬性
clientWidth | 可視區(qū)寬度 |
---|---|
clientHeight | 可視區(qū)高度 |
offsetWidth | 可視區(qū)寬度 |
offsetHeight | 可視區(qū)高度 |
scrollWidth | 內(nèi)容實(shí)際寬度 |
scrollHeight | 內(nèi)容實(shí)際高度 |
scrollTop | 內(nèi)容頂部距離可視區(qū)頂部距離 |
scrollLeft | 內(nèi)容左側(cè)距離可視圖左側(cè)距離 |
比較直觀的示意圖
實(shí)現(xiàn)原理
滾動(dòng)加載的目的是用戶滾動(dòng)頁(yè)面到最底部時(shí)可以自動(dòng)請(qǐng)求下一頁(yè)的數(shù)據(jù)接口,所以問(wèn)題重點(diǎn)是如何確認(rèn)用戶的頁(yè)面滾動(dòng)到了最底部。
列表觸底的條件:可視區(qū)高度 + 滾動(dòng)距離 ≥ 內(nèi)容實(shí)際高度
offsetHeight + scrollTop ≥ scrollHeight
核心代碼
scrollEvent = async (e) => { let scrollHeight = e.target.scrollHeight; let scrollTop = e.target.scrollTop; let offsetHeight = e.target.offsetHeight; if (offsetHeight + scrollTop >= scrollHeight) { console.log('列表觸底,觸發(fā)接口請(qǐng)求數(shù)據(jù)'); this.setState({ loading: true }); let result = await this.loadData(); this.setState({ loading: false, list: this.state.list.concat(result), }); } }; window.addListener('scroll',()=>scrollEvent())
組件封裝
為了讓代碼能夠更高的得到復(fù)用,將代碼封裝成UI組件是必要的,于是我封裝了一個(gè)簡(jiǎn)易的React版的無(wú)限滾動(dòng)組件。
組件能力介紹
- 業(yè)務(wù)方管理數(shù)據(jù)源dataSource,便于個(gè)性化業(yè)務(wù)操作;
- 支持自定義數(shù)據(jù)加載中skeleton;
- 增加觸發(fā)門檻,減少無(wú)效的數(shù)據(jù)請(qǐng)求;
import React, { ReactNode, useEffect, useRef } from 'react' import classnames from 'classnames' interface InfiniteScrollListProps<T> { loading: boolean dataSource: Array<T> renderItem: (data: T) => ReactNode renderSkeleton?: () => ReactNode hasMore: boolean loadMore: () => void className?: string } export default function InfiniteScrollList<T>(props: InfiniteScrollListProps<T>) { const { loading, dataSource, renderItem, renderSkeleton, loadMore, hasMore, className } = props const containerRef = useRef<HTMLDivElement>(null) useEffect(() => { const scrollEvent = (event) => { if (!hasMore || loading) return //可視區(qū)高度 let scrollHeight = event.target?.scrollHeight //滾動(dòng)高度 let scrollTop = event.target.scrollTop //列表內(nèi)容實(shí)際高度 let offsetHeight = event.target.offsetHeight if (offsetHeight + scrollTop >= scrollHeight) { console.log('列表觸底') loadMore() } } containerRef.current?.addEventListener('scroll', scrollEvent) return () => { containerRef.current?.removeEventListener('scroll', scrollEvent) } }, [hasMore, loading]) return ( <div className={classnames('flex-1 flex flex-col overflow-y-auto', className)} ref={containerRef}> {dataSource.map((data) => { return renderItem(data) })} {loading && new Array(4).fill(0).map(() => renderSkeleton?.())} </div> ) }
到此這篇關(guān)于React無(wú)限滾動(dòng)加載列表組件的封裝實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)React無(wú)限滾動(dòng)加載列表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React 的調(diào)和算法Diffing 算法策略詳解
React的調(diào)和算法,主要發(fā)生在render階段,調(diào)和算法并不是一個(gè)特定的算法函數(shù),而是指在調(diào)和過(guò)程中,為提高構(gòu)建workInProcess樹(shù)的性能,以及Dom樹(shù)更新的性能,而采用的一種策略,又稱diffing算法2021-12-12詳解如何在React中優(yōu)雅的使用addEventListener
這篇文章主要為大家詳細(xì)介紹了如何在React中優(yōu)雅的使用addEventListener,文中的示例代碼簡(jiǎn)潔易懂,對(duì)大家學(xué)習(xí)React有一定的幫助,需要的可以參考一下2023-01-01React?Native?Modal?的封裝與使用實(shí)例詳解
這篇文章主要介紹了React?Native?Modal?的封裝與使用,本文通過(guò)實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09react性能優(yōu)化useMemo與useCallback使用對(duì)比詳解
這篇文章主要為大家介紹了react性能優(yōu)化useMemo與useCallback使用對(duì)比詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08