创建无限滚动钩子
如果你曾经使用过移动应用,那么很有可能遇到过“无限滚动”功能。简单来说,当你滚动到指定的 DOM 高度时,会发生一些事情。例如,Twitter 会在你滚动到底部时获取新帖子。
Hooks彻底改变了React 的格局:现在函数式组件可以拥有状态和生命周期方法。自定义 Hooks 还可以复用,为元素添加行为,这终于为HOC及其“包装器地狱”提供了一个绝佳的替代方案。所以,今天我将教你如何创建一个React Hook来实现这个功能。
我们走吧!
我们首先要定义这个钩子应该做什么。首先要做的就是为 添加一个事件监听器window
,因为我们要监视它的scrollHeight
,所以:
import { useEffect, useState } from 'react';
const useInfiniteScroll = (callback: Function) => {
useEffect(() => {
window.addEventListener('scroll', callback);
return () => window.removeEventListener('scroll', callback);
});
}
门槛
现在,callback
每次页面滚动时都会调用该函数,这不符合我们的预期。因此,我们需要添加一个阈值,使其在超过阈值后触发。这将通过一个参数提供,其值应介于 0 到 1 之间:
import { useEffect, useState } from 'react';
const useInfiniteScroll = (callback: Function, threshold: number = 1) => {
useEffect(() => {
const handleScroll = () => {
if (window.innerHeight + document.documentElement.scrollTop
>= document.documentElement.offsetHeight * threshold)
callback();
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [callback]);
}
一个奇怪的错误
核心部分基本完成了。但是,如果在超过“触发点”后继续滚动,你会注意到 会callback
被调用多次。这是因为我们应该确保它会在滚动高度之后被调用,并且只会调用一次。为此,我们可以添加isFetching
:
import { useEffect, useState } from 'react';
const useInfiniteScroll = (callback: Function, threshold: number = 1) => {
const [isFetching, setIsFetching] = useState<Boolean>(false);
useEffect(() => {
const handleScroll = () => {
if (window.innerHeight + document.documentElement.scrollTop
>= document.documentElement.offsetHeight * threshold
&& !isFetching) {
setIsFetching(true);
callback();
}
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [isFetching, callback]);
return [setIsFetching];
}
我们将返回,setIsFetching
以便我们可以控制回调是否完成获取。
最后但并非最不重要的
大多数情况下,无限滚动实际上并不是无限的。所以,当没有更多数据需要获取时,事件监听器就不再需要了,所以最好将其移除:
import { useEffect, useState } from 'react';
const useInfiniteScroll = (callback: Function, threshold: number = 1) => {
const [isFetching, setIsFetching] = useState<Boolean>(false);
const [isExhausted, setIsExhausted] = useState<Boolean>(false);
useEffect(() => {
const handleScroll = () => {
if (window.innerHeight + document.documentElement.scrollTop
>= document.documentElement.offsetHeight * threshold
&& !isFetching) {
setIsFetching(true);
callback();
}
};
if (isExhausted) window.removeEventListener('scroll', handleScroll);
else window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [isFetching, isExhausted, callback]);
return [setIsFetching, isExhausted, setIsExhausted];
}
现在,我们还返回了isExhausted
和setIsExhausted
。第一个可以用来渲染一条消息,第二个用来告诉钩子没有更多数据需要获取。
就是这样
就这样吧,伙计们。希望我能为你们实现这个功能提供一些思路。这种方法对我来说非常有效,尽管它可能不是最巧妙的。
PS:封面取自阿隆·希文的《如何去爱——三个简单的步骤》。
文章来源:https://dev.to/hnrq/creating-an-infinite-scroll-hook-201j