Skip to content

Instantly share code, notes, and snippets.

@TimRChen
Last active August 3, 2021 12:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TimRChen/df6ee0822318205593b3f6957d0a3882 to your computer and use it in GitHub Desktop.
Save TimRChen/df6ee0822318205593b3f6957d0a3882 to your computer and use it in GitHub Desktop.
react 版 useInfiniteScroll
import { useRef, MutableRefObject } from 'react'
import { useUpdateEffect, useUnmount } from 'ahooks'
/**
* 采用Intersection Observer API 实现无限滚动
* more_help: https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API#intersection_observer_%E7%9A%84%E6%A6%82%E5%BF%B5%E5%92%8C%E7%94%A8%E6%B3%95
* @author <timrchen@foxmail.com>
* @param target 目标元素,可在可增加列表末尾设置一个标志标签
* @param callback 正在相交时执行回调,请注意!如果有一些耗时的操作需要执行,建议使用 Window.requestIdleCallback() 方法
* @param options IntersectionObserver()构造函数的 options 对象,可选值参考:https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver/IntersectionObserver#%E5%8F%82%E6%95%B0
*/
export default function useInfiniteScroll(
callback: MutableRefObject<() => void>,
options: IntersectionObserverInit = {},
target: Element | null,
) {
const observer: MutableRefObject<IntersectionObserver | null> = useRef(null)
const executor = ([entry]: IntersectionObserverEntry[]) => {
// 目标元素是否进入可视区域
if (entry && entry.isIntersecting) callback.current()
}
useUpdateEffect(() => {
if (!target) {
throw new Error('intersectTarget "target" must be a Element or a React MutableRefObject')
}
observer.current = new IntersectionObserver(executor, options)
observer.current.observe(target)
}, [options.root, target])
useUnmount(() => observer.current && observer.current.disconnect())
}
@TimRChen
Copy link
Author

TimRChen commented Aug 3, 2021

用法:

  // [tip] 由于相交检测回调函数不会引用最新值,所以我们需要手动检测是否需要更新
  const callback = useRef(props.loadCallback)
  useUpdateEffect(() => (callback.current = props.loadCallback), [props.payload])
  // [tip] https://zh-hans.reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node
  const [root, setRoot] = useSafeState(null)
  const [target, setTarget] = useSafeState(null)
  const rootCallback = useCallback(node => node !== null && setRoot(node), [setRoot])
  const targetCallback = useCallback(node => node !== null && setTarget(node), [setTarget])
  const options: IntersectionObserverInit = { root }
  // 无限滚动
  useInfiniteScroll(callback, options, target)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment