繁体   English   中英

useEffect 中的 IntersectionObserver 只工作一次

[英]IntersectionObserver inside useEffect works only once

我正在使用反应和交叉观察器 api 构建一个无限滚动应用程序。

但我被困在intersectionObserver 只有一次在useEffect 中的部分。

这是代码

import React, { useState, useEffect, createRef } from 'react';
import axios from 'axios';

function App() {
  const [page, setPage] = useState(1);
  const [passengers, setPassengers] = useState([]);
  const bottomRef = createRef();

  const scrollCallback = (entries) => {
    if (entries[0].isIntersecting) {
      axios.get(`https://api.instantwebtools.net/v1/passenger?page=${page}&size=10`).then(res => setPassengers(prevState => [...prevState, ...res.data.data]));
    }
  }; 
  
  useEffect(() => {
    const observer = new IntersectionObserver(scrollCallback, {
      root: null,
      threshold: 1,
    });
    observer.observe(bottomRef.current);
    return () => {
      observer.disconnect();
    }
  }, [bottomRef]);

  return (
    <div className="container">
      <div className="lists">
        {
          passengers.map((passenger, idx) => {
            const { airline, name, trips } = passenger;
              return (
                 <div className="list" key={idx}>
                   <h4>User Info</h4>
                   <p>name: {name}</p>
                   <p>trips: {trips}</p>
                   <h4>Airline Info</h4>
                   <p>name: {airline.name}</p>
                   <p>country: {airline.country}</p>
                   <p>established: {airline.established}</p>
                   <p>head quarter: {airline.head_quarters}</p>
                   <p>website: {airline.website}</p>
                 </div>
              )
            })
        }
      </div>
      <div ref={bottomRef} />
    </div>
  );
}

当滚动到达底部时, scrollCallback中的 scrollCallback function 起作用,它将两个 arrays 合并为一个,并最终显示我想查看的合并数据。

然而,这只工作一次。 useEffect 生命周期不再起作用。 所以我改变了observer.disconnect(); observer.unobserve(bottomRef.current); 但这会发生一个错误说

TypeError: Failed to execute 'unobserve' on 'IntersectionObserver': parameter 1 is not of type 'Element'.

为什么useEffect生命周期无法读取bottomRef

工作代码示例:https://codesandbox.io/s/dazzling-newton-jvivj?file=/src/App.js

奇怪的是codeandbox中的代码有效

如果你可以observe一个参考,你当然也可以unobserve它,只要你有正确的、相同的参考。

useEffect(() => {
  const observer = new IntersectionObserver(scrollCallback, {
    root: null,
    threshold: 1,
  });
  const {current} = bottomRef;
  observer.observe(current);
  return () => {
    observer.unobserve(current);
  };
}, [passengers]);

这永远不会说current不是节点。

此外,如果改变的是passengers ,你最好持有那个参考,而不是持有永远不会改变的东西,因为bottomRefcurrent的包装器,但它不会“永远”改变,而passengers state 将,一旦相交.

交替地

由于bottomRef并不意味着改变,您也可以只观察该节点而不清理它的观察结果。

useEffect(() => {
  const observer = new IntersectionObserver(scrollCallback, {
    root: null,
    threshold: 1,
  });
  observer.observe(bottomRef.current);
}, [bottomRef]);

无论哪种方式都应该解决问题。

您需要使用useRef ,而不是createRef正如 Andrea 所说,您需要使用与使用相同的元素unobserveobserve一起使用。 最后,由于您的useEffect回调使用的变化是bottomRef.current ,这就是要在您的依赖项数组中使用的依赖项。

这是一个工作示例,请参阅***评论:

 const { useState, useEffect, useRef } = React; function App() { const [page, setPage] = useState(1); const [passengers, setPassengers] = useState([]); const bottomRef = useRef(null); // *** Not `createRef` const scrollCallback = (entries) => { if (entries[0].isIntersecting) { getMorePassengers().then(res => setPassengers(prevState => [...prevState, ...res.data.data])); // *** Should handle/report errors here } }; useEffect(() => { // *** Grab the element related to this callback const { current } = bottomRef; const observer = new IntersectionObserver(scrollCallback, { root: null, threshold: 1, }); observer.observe(current); return () => { observer.disconnect(current); // *** Use the same element } }, [bottomRef.current]); // *** Note dependency return ( <div className="container"> <div className="lists"> { passengers.map((passenger, idx) => { // *** I simplified this for the example const { name } = passenger; return ( <div className="list" key={idx}> <p>name: {name}</p> </div> ) }) } </div> <div ref={bottomRef} /> </div> ); } // ** Stand-in for ajax let passengerCounter = 0; function getMorePassengers() { return new Promise(resolve => { resolve({ data: { data: [ // Obviously, you don't use things like `passengerCounte` {name: `Passenger ${passengerCounter++}`}, {name: `Passenger ${passengerCounter++}`}, {name: `Passenger ${passengerCounter++}`}, {name: `Passenger ${passengerCounter++}`}, {name: `Passenger ${passengerCounter++}`}, ] } }); }); } ReactDOM.render(<App/>, document.getElementById("root"));
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

但是你可能根本不需要那个清理回调。 Andrea GiammarchiIntersectionObserver对被观察的元素使用了弱引用,并且 A) 他对这样的事情几乎总是正确的,所以我相信他; B)这样设计是有道理的。 所以你可以完全放弃清理回调。

另外,我希望您正在观察的div只会在安装组件时创建一次,之后再也不会重新创建。 所以理论上你可以使用[]作为你的依赖数组。

也就是说,如果 React 有某种理由重新创建该div ,我会学习保留依赖关系,并且我更喜欢显式清理,所以我可能会离开它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM