【问题标题】:React Infinite Loading hook, previous triggerReact Infinite Loading 钩子,前一个触发器
【发布时间】:2021-12-31 02:17:05
【问题描述】:

我正在尝试制作类似于 Waypoint 的钩子。

我只是想加载项目,然后当路点不在屏幕上时,如果到达路点,则允许它加载更多项目。

我似乎无法弄清楚让这项工作正常工作的逻辑。

目前它在屏幕上看到观察者状态。然后它会快速获取数据。

我认为这是因为钩子每次都从 false 开始。我不确定如何使它成为现实,以便可以加载数据。再次到达时则相反。

任何想法。

这是钩子:

import { useEffect, useState, useRef, RefObject } from 'react';

export default function useOnScreen(ref: RefObject<HTMLElement>) {
  const observerRef = useRef<IntersectionObserver | null>(null);
  const [isOnScreen, setIsOnScreen] = useState(false);

  useEffect(() => {
    observerRef.current = new IntersectionObserver(([entry]) => {
      if (isOnScreen !== entry.isIntersecting) {
        setIsOnScreen(entry.isIntersecting);
      }
    });
  }, []);

  useEffect(() => {
    observerRef.current.observe(ref.current);

    return () => {
      observerRef.current.disconnect();
    };
  }, [ref]);

  return isOnScreen;
}

这是它的用法:

import React, { useRef } from 'react';
import { WithT } from 'i18next';

import useOnScreen from 'utils/useOnScreen';

interface IInboxListProps extends WithT {
  messages: any;
  fetchData: () => void;
  searchTerm: string;
  chatID: string | null;
}

const InboxList: React.FC<IInboxListProps> = ({ messages, fetchData, searchTerm, chatID}) => {
  const elementRef = useRef(null);
  const isOnScreen = useOnScreen(elementRef);

  if (isOnScreen) {
    fetchData();
  }

  
  const renderItem = () => {
    return (
      <div className='item unread' key={chatID}>
      Item
      </div>
    );
  };

  const renderMsgList = ({ messages }) => {
    return (
      <>
        {messages.map(() => {
          return renderItem();
        })}
      </>
    );
  };

  let messagesCopy = [...messages];

  //filter results
  if (searchTerm !== '') {
    messagesCopy = messages.filter(msg => msg.user.toLocaleLowerCase().startsWith(searchTerm.toLocaleLowerCase()));
  }

  return (
    <div className='conversations'>
      {renderMsgList({ messages: messagesCopy })}
      <div className='item' ref={elementRef} style={{ bottom: '10%', position: 'relative',backgroundColor:"blue",width:"5px",height:"5px" }} />
    </div>
  );
};

export default InboxList;

【问题讨论】:

    标签: reactjs react-native react-hooks infinite-scroll jquery-waypoints


    【解决方案1】:

    让我们检查一下这段代码

      const [isOnScreen, setIsOnScreen] = useState(false);
    
      useEffect(() => {
        observerRef.current = new IntersectionObserver(([entry]) => {
          if (isOnScreen !== entry.isIntersecting) {
            setIsOnScreen(entry.isIntersecting);
          }
        });
      }, []);
    

    我们有以下含义:

    • .isIntersecting 为 TRUE --> 元素变得可见
    • .isIntersecting 为 FALSE --> 元素消失了

    • isOnScreen 为 TRUE --> 元素至少可见一次
    • isOnScreen 为 FALSE--> 元素从不可见

    当使用异或 (!==) 时,您指定它:

    • 从不可见,只是变得可见
      • 在第一个交叉路口后发生 1 次
    • 曾经可见,现在消失了
      • 每次元素离开屏幕时都会发生 n 次

    你想要做的是每次元素相交时获取更多的项目

    export default function useOnScreen(ref: RefObject<HTMLElement>, onIntersect: function) {
      const observerRef = useRef<IntersectionObserver | null>(null);
      const [isOnScreen, setIsOnScreen] = useState(false);
    
      useEffect(() => {
        observerRef.current = new IntersectionObserver(([entry]) => {
          setIsOnScreen(entry.isIntersecting);
        });
      }, []);
      
      useEffect(()=?{
        if(isOnScreen){
           onIntersect();
        }
      },[isOnScreen,onIntersect])
    
      ...
    }
    

    然后像这样使用它:

      const refetch= useCallback(()=>{
        fetchData();
      },[fetchData]);
    
      const isOnScreen = useOnScreen(elementRef, refetch);
    

    或者简单地说:

      const isOnScreen = useOnScreen(elementRef, fetchData);
    

    如果fetchData 出于某种原因更改了引用,您可能需要改用以下内容:

      const refetch= useRef(fetchData);
    
      const isOnScreen = useOnScreen(elementRef, refetch);
    

    记住 useOnScreen 必须像 onIntersect.current() 一样调用它

    【讨论】:

    • 绝对是对我所做工作的改进。问题是 Fetch 仍然在负载和 Fetch More 上被调用很多次。我认为这是因为组件树中的消息列表位置:```收件箱-InboxPanel(消息的初始加载和fetchmore)--InboxList(显示messgaes并在到达航点时执行fetchmore)```跨度>
    • 我在想也许跟踪以前的位置并比较将是关键。如果您同意,这将如何在您的解决方案中发挥作用
    • 我认为跟踪太难实现而且矫枉过正。我认为问题在于fetchData 被取消引用太频繁。您是否尝试过我发布的最新代码? (useRef(fetchData)的那个)
    • 参考版本有效。谢谢!
    【解决方案2】:

    InboxList 组件中,这段代码我们在说什么

      if (isOnScreen) {
        fetchData();
      }
    

    也就是说,每次InboxList 渲染时,如果航点在屏幕上,则启动获取,无论之前的获取是否仍在进行中。

    请注意,InboxList 可能会在进行提取时重新渲染,可能会多次重新渲染,原因有很多,例如父组件重新渲染。只要航点在屏幕上,每次重新渲染都会启动新的获取。

    为了防止这种情况,我们需要跟踪正在进行的获取,例如典型的isLoading 状态变量。然后仅在isLoading === false &amp;&amp; isOnScreen 时才开始新的提取。

    或者,如果保证每次获取都会将航点推离屏幕,那么我们可以仅在航点coming在屏幕上时启动获取,即​​isOnScreen变为true来自false

    useEffect(() => {
      if (isOnScreen) {
        fetchData();
      }    
    }, [isOnScreen]);
    

    但是,如果我们假设航点在每次获取时都超出屏幕的假设不成立,那么这将无法正常运行。这可能是因为

    • pageSize的取小,显示区域可以容纳更多 元素
    • 从 fetch 接收的数据被过滤掉,因为 客户端过滤,例如searchTerm

    【讨论】:

      【解决方案3】:

      作为我的假设。你也可以试试这个方法。

         const observeRef = useRef(null);
          const [isOnScreen, setIsOnScreen] = useState(false);
          const [prevY, setPrevY] = useState(0);
          
           
        useEffect(()=>{
          fetchData();
      
        var option = {
        root : null,
        rootmargin : "0px",
        threshold : 1.0 };
      
        const observer =  new IntersectionObserver(
            handleObserver(),
            option
          );
         
       const handleObserver = (entities, observer) => {
          const y = observeRef.current.boundingClientRect.y;
          if (prevY > y) { 
             fetchData();
          } 
          setPrevY(y);
        } 
      
      
      },[prevY]);
         
      

      在这种情况下,我们不关注聊天消息。我们只关注 chat&lt;div className="item 元素下方。当滚动条触发 div 元素时,fetchData() 一次又一次地调用.. 解释:

      -首先在 hanlderObserver 中你可以看到boundingClientRect.yboundingClientRect 方法读取元素位置。在这种情况下,我们只需要 y 轴,因为使用 y。

      • 当滚动条到达 div 元素时,y 值发生变化。然后再次触发 fetchData()。

      • root :这是用于交集的根。 rootMargin :就像一个边距属性,用于为根提供边距值,以像素或百分比 (%) 为单位。 threshold :当交叉点的面积变为大于或等于我们在此示例中提供的值时,用于触发回调的数字。 最后你可以添加加载状态来加载数据。

             return (
                 <div className='conversations'>
                   {renderMsgList({ messages: messagesCopy })}
                   <div className='item' ref={observeRef} style={{ bottom: '10%', position: 'relative',backgroundColor:"blue",width:"5px",height:"5px" }} />
        
                 </div>
               );
        

        };

      我希望它是正确的,我不确定。可能对某人有帮助。谢谢你..

      【讨论】:

        猜你喜欢
        • 2015-07-18
        • 1970-01-01
        • 2020-08-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-07
        相关资源
        最近更新 更多