创建无限滚动钩子

2025-06-07

创建无限滚动钩子

如果你曾经使用过移动应用,那么很有可能遇到过“无限滚动”功能。简单来说,当你滚动到指定的 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);
  });
}
Enter fullscreen mode Exit fullscreen mode

门槛

现在,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]);
}
Enter fullscreen mode Exit fullscreen mode

一个奇怪的错误

核心部分基本完成了。但是,如果在超过“触发点”后继续滚动,你会注意到 会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];
}
Enter fullscreen mode Exit fullscreen mode

我们将返回,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];
}
Enter fullscreen mode Exit fullscreen mode

现在,我们还返回了isExhaustedsetIsExhausted。第一个可以用来渲染一条消息,第二个用来告诉钩子没有更多数据需要获取。

就是这样

就这样吧,伙计们。希望我能为你们实现这个功能提供一些思路。这种方法对我来说非常有效,尽管它可能不是最巧妙的。

PS:封面取自阿隆·希文的《如何去爱——三个简单的步骤》。

文章来源:https://dev.to/hnrq/creating-an-infinite-scroll-hook-201j
PREV
不要成为程序员,而要成为软件工程师
NEXT
你可以使用这些技巧将 Web 应用程序的大小减少数倍🔥