使用Google Voice服务可以免费拨打美国或加拿大境内的任何电话,而国际电话(非北美地区)的费用也比传统电话便宜,比如拨打中国大陆的价格为1美分/分钟,香港的价格为2美分/分钟,台湾市话:2美...
【Day17】数据展示元件 - Infinite scroll
元件介绍
Infinite scroll
能在面对多笔资料时,让卷轴滑动到底部时再载入下一页面的资料。
由於一次性向後端取得大批的资料,对於後端的资料计算、资料透过网路传输、页面的渲染,在效能上都有可能会有影响,因此将资料分批载入也有助於网页效能的优化。
另一个分批载入常见的做法是使用 Pagination,分页载入,虽然都是分批载入,但是使用情境有一些区别, Infinite scroll 的优点是一直往下滑就会自动有资料载入,操作效率较流畅,但缺点是难以回去找刚刚看过的东西。所以如果网页内容是希望让使用者能够有效率地找寻特定资讯时,这时选择 Pagination 会比 Infinite scroll 较为适合。
参考设计 & 属性分析
Infinite scroll 的特点是让资料滚到底部时自动载入,所以这边的关键是,我们要如何判断「是否已经滚动到底部」?
从上图可以知道,Element.scrollHeight
表示元件的可滚动范围;Element.scrollTop
指的是元素被向上滚动的高度,换句话说就是你已经走过的距离;最後 Element.clientHeight
就是指元素内部高度,也就是滚动可视范围的高度。
Element.scrollTop + Element.clientHeight >= Element.scrollHeight
所以「滚动到底部」换句话来说,就是你滚过的距离加上自己元素的高度,大於等於可滚动范围的高度。
介面设计
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
height | 元件高度 | number | |
isLoading | 载入中状态 | boolean | false |
onScrollBottom | 滑动到底部的 callback | function | |
children | 内容 | list of ReactNode |
元件实作
以下是我们想像当中的 InfiniteScroll
,在元件的 children 当中就是被浏览的内容,所以被 InfiniteScroll
包起来的内容我们希望被不断的载入。
因为他是一个可被滑动的范围,所以这个容器需要被限制高度,内容超出这个高度才有办法被 scroll。
再来我们需要在滑动到底部的时候触发事件,例如需要去打某支 API 来载入资料,因此我们提供一个 onScrollBottom
的 callback。
然後我们在打 API 的时候,是一个非同步行为,会有载入中的状态,因此也有一个 isLoading
的 Boolean props:
<InfiniteScroll
height={250}
isLoading={isLoading}
onScrollBottom={() => {}}
>
{...}
</InfiniteScroll>
如下程序码所示,我们需要透过 useRef
让我们能够操作这个容器的 DOM,因为我们需要计算何时滑动到底部:
const infiniteScrollRef = useRef();
<InfiniteScrollWrapper
ref={infiniteScrollRef}
$height={height}
onScroll={handleOnScroll}
>
{children}
{isLoading && <Loading />}
</InfiniteScrollWrapper>
前面分析我们也已经介绍过如何判断滑动到底部的方法,因此在 onScroll 的时候,我们需要去触发这个计算:
const handleOnScroll = () => {
const containerElem = infiniteScrollRef.current;
if (containerElem) {
const scrollPos = containerElem.scrollTop + containerElem.clientHeight;
const divHeight = containerElem.scrollHeight;
// 滚过的距离加上自己元素的高度,大於等於可滚动范围的高度
if ((scrollPos >= divHeight) && onScrollBottom) {
onScrollBottom();
}
}
};
这样我们简单的 InfiniteScroll
就搞定了!
我们展示一下成果:
从上图来看,当我们滑动到底部的时候,就会去触发 GET api 来取得下一页的资料,并且将资料更新到画面上。
我使用的方式是,在 onScrollBottom 被呼叫的时候,表示他滑到底部了,所以我要取得下一页的资料,因此我透过 setPage
这个 useState 将 page + 1
:
const [page, setPage] = useState(1);
<InfiniteScroll
height={250}
isLoading={isLoading}
onScrollBottom={() => {
if (!isLoading) {
setPage((prev) => prev + 1);
}
}}
>
{
dataSource.map(({ id, author, download_url }) => (
<ListItem
key={id}
author={author}
url={download_url}
/>
))
}
</InfiniteScroll>
当 page 这个 state 被改变的时候,我就要去打 API 来载入资料,所以我这便是透过 useEffect
来实作,并且他的 comparison array 里面就放了 page,表示 page 被改变的时候,需要执行里面的内容:
useEffect(() => {
setSideEffect({
...defaultSideEffect,
isLoading: true,
});
fetch(`https://picsum.photos/v2/list?page=${page}&limit=${limit}`, {})
.then((response) => {
setSideEffect({
...defaultSideEffect,
isLoaded: true,
});
return response.json();
})
.then((jsonData) => {
setDataSource((prev) => [...prev, ...jsonData]);
}).catch((error) => {
setSideEffect({
...defaultSideEffect,
error,
});
});
}, [page]);
InfiniteScroll 元件原始码:
Source code
Storybook:
InfiniteScroll
参考
https://codesandbox.io/s/yk7637p62z?file=/src/index.js
国内公司、香港公司、美国公司是目前绝大部分跨境卖家的身份选择。国内公司身份自然不必多说,90%+都是。香港和美国公司则少的多。但选择的人多并不表示国内公司就是最佳的经营跨境电商身份选择。 国内公司 首...
在深入探讨提高网站速度之前,让我们探讨一下为什么它对您的小型企业如此重要。 更快的网站意味着: 更好的用户体验 您的网站性能会影响用户的体验 - 当您的网站加载速度更快时,用户更有可能与之互动并花费更...
网站速度优化对于创造积极的用户体验至关重要。 积极的用户体验是快乐用户的营销代言词。 快乐的用户访问您的网站并购买东西。 不满意的用户离开是因为他们厌倦了等待您的网站加载。 营销人员称之为“跳出率”...
为什么网站速度很重要 到目前为止,您应该不需要说服网站速度对您的在线业务至关重要。 这是因为网站性能会影响您的品牌声誉、SEO 排名和转化率。 以下是发生这种情况的主要原因: 品牌口碑👍 老实说,当...