【问题标题】:Is it possible to avoid 'eslint(react-hooks/exhaustive-deps)' error on custom React Hook with useCallback?是否可以通过 useCallback 避免自定义 React Hook 上的“eslint(react-hooks/exhaustive-deps)”错误?
【发布时间】:2020-12-02 09:27:10
【问题描述】:

使用以下自定义 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 的正确方法是什么?

【问题讨论】:

    标签: reactjs react-hooks eslint


    【解决方案1】:

    解决此问题的方法是从 options 解构您需要的属性并将它们设置在依赖数组中。这样你就不需要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 };
    

    【讨论】:

    • 这也不起作用,但你的建议让我思考,我通过破坏函数参数解决了这个问题,还从IntersectionObserver 中删除了options 并再次构造了对象。
    • @rfgamaral 另一个选项是记下options 对象并将其传递到钩子中
    • @MattAft 你能提供一个完整的例子来记录文档吗?
    • @rfgamaral 发布了一个答案,让我知道是否可以为您解决问题
    【解决方案2】:

    您应该始终在 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); 
    

    【讨论】:

    • 知道了。与其他解决方案相比有哪些优点/缺点?
    • 在这种情况下差别不大,因为你的钩子非常轻量级。但是可以说对象可以有不同的值,所有的钩子都会关心是否传入了新的 obj,这可以由父级中的 memo'd obj 确定
    • 谢谢。我想这两个答案都适用于我的用例。但我理解它们的工作方式略有不同:)
    猜你喜欢
    • 2021-04-15
    • 2023-03-24
    • 2020-06-22
    • 2020-06-09
    • 2021-09-03
    • 2022-08-18
    • 2020-01-18
    • 2021-04-18
    • 2020-05-01
    相关资源
    最近更新 更多