怎样确保 ref.current 永远不为 null

huangapple go评论67阅读模式
英文:

How can ref.current never be null

问题

谢谢阅读。
我正在开发一个React应用程序。

使用“IntersectionObserver”观察多个“ref”。
代码中被注释掉的部分将为null。
知道为什么吗?

英文:

thank you for reading.
I am developing a React app.

Use "IntersectionObserver" to observe multiple "ref".
Commented out "*" parts in the code will be null.
Do you know why?

const refs = useRef<RefObject<HTMLVideoElement>[]>([]);

useEffect(() => {
    const observer = new IntersectionObserver(entry => {
    });

    fetchData().then(results => {
        // set data here

        results.forEach((d, i) => refs.current[i] = createRef<HTMLVideoElement>());

        refs.current.forEach(ref => {
            console.log(ref.current);  // * 
            if (ref.current) {
                observer.observe(ref.current);
            }
        });
    }).catch(error => {
        alert("error");
    });

    return () => {
        refs.current.forEach((ref) => {
            if (ref.current) {
                observer.unobserve(ref.current)
            }
        });
    };
}, []);

By the way, "ref" instead of "ref.current" looks like the attached image.
怎样确保 ref.current 永远不为 null

答案1

得分: 1

以下是翻译好的部分:

原文:

Immediately after creating the refs, you try to use them, without ever assigning them to any element. Even if you do assign them to elements, the assignment would happen after you tried using them.

Instead use function refs to observe elements - the observer is stored in a ref. Whenever one of the observe callbacks is called, it checks if observer.current exists, and if not it initializes it. It then registers with the observer the element that used it as ref.

Don't bother cleaning individual observed elements, since the observer has weak links to the observed, so removing the elements won't cause a memory leak. Use a useEffect to terminate the observer as a cleanup measure.

In addition, now you can add more items and pass them the ref callback (observer).

翻译:

在创建引用后,立即尝试使用它们,而不将它们分配给任何元素。即使您将它们分配给元素,分配也会在尝试使用它们之后发生。

相反,使用函数引用来观察元素 - 观察器存储在引用中。每当调用observe回调函数之一时,它都会检查observer.current是否存在,如果不存在,则进行初始化。然后,它会向观察器注册使用它作为引用的元素。

不必担心清理单个观察的元素,因为观察器与观察的元素之间有弱引用,因此删除元素不会导致内存泄漏。使用useEffect来终止观察器作为清理措施。

此外,现在您可以添加更多项目并将引用回调(observer)传递给它们。

英文:

Immediately after creating the refs, you try to use them, without ever assigning them to any element. Even if you do assign them to elements, the assignment would happen after you tried using them.

Instead use function refs to observe elements - the observer is stored in a ref. Whenever one of the observe callbacks is called, it checks if observer.current exists, and if not it initializes it. It then registers with the observer the element that used it as ref.

Don't bother cleaning individual observed elements, since the observer has weak links to the observed, so removing the elements won't cause a memory leak. Use a useEffect to terminate the observer as a cleanup measure.

In addition, now you can add more items and pass them the ref callback (observer).

<!-- begin snippet: js hide: false console: true babel: true -->

<!-- language: lang-js -->

const { useRef, useCallback, useEffect } = React;

const Demo = () =&gt; {
  const observer = useRef();
  
  const observe = useCallback(el =&gt; {
    if(!observer.current) { // init the observer if needed
      observer.current = new IntersectionObserver(entry =&gt; {
        console.log(entry);
      });
    }

    observer.current.observe(el); // observe the element
  }, []);

  useEffect(() =&gt; () =&gt; { // cleanup
    observer.current.disconnect();
  }, []);
  
  return (
    &lt;div&gt;
      &lt;div ref={observe}&gt;a&lt;/div&gt;
      &lt;div ref={observe}&gt;b&lt;/div&gt;
      &lt;div ref={observe}&gt;c&lt;/div&gt;
    &lt;/div&gt;
  );
};

ReactDOM
  .createRoot(root)
  .render(&lt;Demo /&gt;);

<!-- language: lang-html -->

&lt;script crossorigin src=&quot;https://unpkg.com/react@18/umd/react.development.js&quot;&gt;&lt;/script&gt;
&lt;script crossorigin src=&quot;https://unpkg.com/react-dom@18/umd/react-dom.development.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;

<!-- end snippet -->

You can extract this logic to a custom hook. The only change is to store the external callback function cb in a ref, so it won't be a dependency of the observe function:

<!-- begin snippet: js hide: false console: true babel: true -->

<!-- language: lang-js -->

const { useRef, useCallback, useEffect } = React;

const useIntersection = cb =&gt; {
  const observer = useRef();
  const fn = useRef(cb);
  
  useEffect(() =&gt; { // store the callback function in a ref
    fn.current = cb;
  });

  useEffect(() =&gt; () =&gt; { // cleanup
    observer.current.disconnect();
  }, []);
  
  return useCallback(el =&gt; {
    if(!observer.current) { // init the observer if needed
      observer.current = new IntersectionObserver(entry =&gt; {
        fn.current(entry);
      });
    }

    observer.current.observe(el); // observe the element
  }, []);
};

const Demo = () =&gt; {
  const observe = useIntersection(console.log);
  
  return (
    &lt;div&gt;
      &lt;div ref={observe}&gt;a&lt;/div&gt;
      &lt;div ref={observe}&gt;b&lt;/div&gt;
      &lt;div ref={observe}&gt;c&lt;/div&gt;
    &lt;/div&gt;
  );
};

ReactDOM
  .createRoot(root)
  .render(&lt;Demo /&gt;);

<!-- language: lang-html -->

&lt;script crossorigin src=&quot;https://unpkg.com/react@18/umd/react.development.js&quot;&gt;&lt;/script&gt;
&lt;script crossorigin src=&quot;https://unpkg.com/react-dom@18/umd/react-dom.development.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年2月24日 15:02:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/75553474.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定