Costume Hook says array of elements is not iterable

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

Costume Hook says array of elements is not iterable

问题

我正在尝试创建一个自定义钩子,用于调用IntersectionObserver来观察ref内的所有li元素。

当我在组件中直接使用它时,它运行得很好:

const App = () => {
  const array = [...Array(50).keys()];
  const ref = React.useRef(null);

  React.useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach((element) => {
        if (element.isIntersecting) {
          element.target.style.color = 'green';
        }
      });
    });

    ref.current.querySelectorAll(`li`).forEach((element) => {
      observer.observe(element);
    });
  }, []);

  return (
    <ul ref={ref}>
      {array.map((_, i) => <li key={i}>{i}</li>)}
    </ul>
  );
}

但当我将它分离到一个自定义钩子中时,它报错说它不可迭代:

const App = () => {
  const array = [...Array(50).keys()];
  const ref = React.useRef(null);

  const useCustomHook = (elements) => {
    React.useEffect(() => {
      const observer = new IntersectionObserver((entries) => {
        entries.forEach((element) => {
          if (element.isIntersecting) {
            element.target.style.color = 'green';
          }
        });
      });

      elements.forEach((element) => {
        observer.observe(element);
      });
    }, []);
  }

  useCustomHook(ref?.current.querySelectorAll(`li`));

  return (
    <ul ref={ref}>
      {array.map((_, i) => <li key={i}>{i}</li>)}
    </ul>
  );
}

是否有一种方式可以通过给自定义钩子一个元素数组来实现这一点?

谢谢! 😊

英文:

I am trying to create a costume hook that calls IntersectionObserver for all of the li elements inside a ref.

When I just use it in the component, it works great:

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

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

const App = () =&gt; {

  const array = [...Array(50).keys()];
  const ref = React.useRef(null);
  
  
  React.useEffect(() =&gt; {

        const observer = new IntersectionObserver((entries) =&gt; {
            entries.forEach((element: any) =&gt; {
                if (element.isIntersecting) {
                         element.target.style.color = &#39;green&#39;;
                }
            })

        })

        ref.current.querySelectorAll(`li`).forEach(element =&gt; {
            observer.observe(element);

        });
}, [ ]);
  
  return (
    &lt;ul ref={ref}&gt;
        {array.map((_, i) =&gt; &lt;li key={i}&gt;{i}&lt;/li&gt;)}
    &lt;/ul&gt;
  )
}

ReactDOM.createRoot(document.getElementById(`root`))
  .render(
    &lt;React.StrictMode&gt;
      &lt;App /&gt;
    &lt;/React.StrictMode&gt;
  );

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

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js&quot;&gt;&lt;/script&gt;&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js&quot;&gt;&lt;/script&gt;&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;

<!-- end snippet -->

But When I separate it into a costume hook, it says it is not iterable:

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

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

const App = () =&gt; {

  const array = [...Array(50).keys()];
  const ref = React.useRef(null);
  
  const useCostumeHook = (elements) =&gt; {
  
    React.useEffect(() =&gt; {

        const observer = new IntersectionObserver((entries) =&gt; {
            entries.forEach((element: any) =&gt; {
                if (element.isIntersecting) {
                         element.target.style.color = &#39;green&#39;;
                }
            })

        })

        elements.forEach(element =&gt; {
            observer.observe(element);

        });
}, [ ]);
  
  }
  
useCostumeHook(ref?.current.querySelectorAll(`li`))
  
  return (
    &lt;ul ref={ref}&gt;
        {array.map((_, i) =&gt; &lt;li key={i}&gt;{i}&lt;/li&gt;)}
    &lt;/ul&gt;
  )
}

ReactDOM.createRoot(document.getElementById(`root`))
  .render(
    &lt;React.StrictMode&gt;
      &lt;App /&gt;
    &lt;/React.StrictMode&gt;
  );

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

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js&quot;&gt;&lt;/script&gt; &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;

<!-- end snippet -->

Is there a way to do this with just giving the costume hook an array of elements?

THANKS! 😊

答案1

得分: 0

当你调用这个钩子,并传递ref.current?.querySelectorAll(li)ref.current仍然是null,所以结果是undefined,无法迭代。

相反,传递一个函数,允许useEffect块在ref已经设置时获取项目:

const useCustomHook = (getElements) => {
  React.useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach((element) => {
        if (element.isIntersecting) {
          element.target.style.color = 'green';
        }
      });
    });

    const elements = getElements();

    elements?.forEach((element) => {
      observer.observe(element);
    });
  }, []);
};

用法:

useCustomHook(() => ref.current?.querySelectorAll(`li`));
英文:

When you call the hook, and pass ref.current?.querySelectorAll(li), the ref.current is still null, so the result is undefined which not iterable.

Instead pass a function that would allow the useEffect block to get the items when the ref is already set:

const useCostumeHook = (getElements) =&gt; {
  React.useEffect(() =&gt; {
    const observer = new IntersectionObserver((entries) =&gt; {
      entries.forEach((element: any) =&gt; {
        if (element.isIntersecting) {
          element.target.style.color = &#39;green&#39;;
        }
      })
    })
    
    const elements = getElements();

    elements?.forEach(element =&gt; {
      observer.observe(element);
    });
  }, []);
}

Usage:

useCostumeHook(() =&gt; ref.current?.querySelectorAll(`li`))

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

发表评论

匿名网友

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

确定