使用以下自定义React Hook与IntersectionObserver
:交互
import { useCallback, useRef, useState } from 'react';
type IntersectionObserverResult = [(node: Element | null) => void, IntersectionObserverEntry?];
function useIntersectionObserver(options: IntersectionObserverInit): IntersectionObserverResult {
const intersectionObserver = useRef<IntersectionObserver>();
const [entry, setEntry] = useState<IntersectionObserverEntry>();
const ref = useCallback(
(node) => {
if (intersectionObserver.current) {
console.log('[useInterSectionObserver] disconnect(🔴)');
intersectionObserver.current.disconnect();
}
if (node) {
intersectionObserver.current = new IntersectionObserver((entries) => {
console.log('[useInterSectionObserver] callback(🤙)');
console.log(entries[0]);
setEntry(entries[0]);
}, options);
console.log('[useInterSectionObserver] observe(🟢)');
intersectionObserver.current.observe(node);
}
},
[options.root, options.rootMargin, options.threshold]
);
return [ref, entry];
}
export { useIntersectionObserver };
ESLint正在抱怨:
React Hook useCallback缺少依赖项:"options"。请将其包括在内或删除依赖项数组。
如果我用[options]
替换依赖项数组,ESLint不再抱怨,但现在有一个更大的问题,渲染无限循环。
在不出现eslint(react-hooks/exhaustive-deps)
错误的情况下,实现这个自定义React Hook的正确方法是什么?
解决方法是从options
中销毁所需的属性,并将其设置在dependency数组中。这样就不需要options
,并且只有当这三个值发生变化时才会调用钩子。
import { useCallback, useRef, useState } from 'react';
type IntersectionObserverResult = [(node: Element | null) => void, IntersectionObserverEntry?];
function useIntersectionObserver(options: IntersectionObserverInit): IntersectionObserverResult {
const intersectionObserver = useRef<IntersectionObserver>();
const [entry, setEntry] = useState<IntersectionObserverEntry>();
const { root, rootMargin, threshold } = options;
const ref = useCallback(
(node) => {
if (intersectionObserver.current) {
console.log('[useInterSectionObserver] disconnect(🔴)');
intersectionObserver.current.disconnect();
}
if (node) {
intersectionObserver.current = new IntersectionObserver((entries) => {
console.log('[useInterSectionObserver] callback(🤙)');
console.log(entries[0]);
setEntry(entries[0]);
}, options);
console.log('[useInterSectionObserver] observe(🟢)');
intersectionObserver.current.observe(node);
}
},
[root, rootMargin, threshold]
);
return [ref, entry];
}
export { useIntersectionObserver };
您应该始终在dep数组中提供所有必要的值,以防止它使用带有过时值的前一个缓存函数。解决这种情况的一个选项是记下options
对象,这样当它的值发生变化时,而不是每次重新渲染时,只传递一个新对象:
// in parent
// this passes a new obj on every re-render
const [ref, entry] = useIntersectionObserver({ root, rootMargin, threshold });
// this will only pass a new obj if the deps change
const options = useMemo(() => ({ root, rootMargin, threshold }), [root, rootMargin, threshold]);
const [ref, entry] = useIntersectionObserver(options);