1 引言
InterpObserver 可以轻松判断元素是否可见,在之前的 精读《用 React 做按需渲染》 中介绍了原生 API 的方法,这次刚好看到其 React 封装版本 react-interp-observer,让我们看一看 React 封装思路。
2 简介
react-interp-observer 提供了 Hook useInView
判断元素是否在可视区域内,API 如下:
import React from "react";
import { useInView } from "react-interp-observer";
const Component = () => {
const [ref, inView] = useInView();
return (
<div ref={ref}>
<h2>{`Header inside viewport ${inView}.`}</h2>
</div>
);
};
由于判断元素是否可见是基于 dom 的,所以必须将 ref
回调函数传递给 代表元素轮廓的 DOM 元素,上面的例子中,我们将 ref
传递给了最外层 DIV。
useInView
还支持下列参数:
root
:检测是否可见基于的视窗元素,默认是整个浏览器 viewport。rootMargin
:root 边距,可以在检测时提前或者推迟固定像素判断。threshold
:是否可见的阈值,范围 0 ~ 1,0 表示任意可见即为可见,1 表示完全可见即为可见。triggerOnce
:是否仅触发一次。
3 精读
首先从入口函数 useInView
开始解读,这是一个 Hook,利用 ref
存储上一次 DOM 实例,state
则存储 inView
元素是否可见的 boolean 值:
export function useInView(
options: InterpOptions = {},
): InViewHookResponse {
const ref = React.useRef<Element>()
const [state, setState] = React.useState<State>(initialState)
// 中间部分..
return [setRef, state.inView, state.entry]
}
当组件 ref 被赋值时会调用 setRef
,回调 node
是新的 DOM 节点,因此先 unobserve(ref.current)
取消旧节点的监听,再 observe(node)
对新节点进行监听,最后 ref.current = node
更新旧节点:
// 中间部分 1
const setRef = React.useCallback(
(node) => {
if (ref.current) {
unobserve(ref.current);
}
if (node) {
observe(
node,
(inView, interp) => {
setState({ inView, entry: interp });
if (inView && options.triggerOnce) {
// If it should only trigger once, unobserve the element after it's inView
&nbs