React Infinite Loading 钩子,前一个触发器
React Infinite Loading hook, previous trigger
我正在尝试制作一个类似于 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;
让我们检查一下这段代码
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()
那样调用它
在InboxList
组件中,这段代码表达的意思
if (isOnScreen) {
fetchData();
}
就是每次InboxList
渲染时,如果屏幕上有航路点,则启动抓取,不管之前的抓取是否仍在进行中。
请注意,由于多种原因,InboxList
可能会在获取过程中重新呈现,可能会多次呈现,例如父组件重新渲染。只要航路点在屏幕上,每次重新渲染都会启动新的获取。
为了防止这种情况,我们需要跟踪正在进行的提取,类似于典型的 isLoading
状态变量。然后仅在 isLoading === false && isOnScreen
.
时启动新的提取
或者,如果保证每次获取都会将路径点推离屏幕,那么我们可以仅在路径点即将出现在屏幕上时才启动获取,即isOnScreen
正在从 false
更改为 true
:
useEffect(() => {
if (isOnScreen) {
fetchData();
}
}, [isOnScreen]);
但是,如果我们的假设(航点在每次获取时都离开屏幕)不成立,则这将无法正常运行。这可能发生,因为
pageSize
的fetch small and display area可以容纳更多
元素
- 由于以下原因,从提取中收到的数据被过滤掉
客户端过滤,例如
searchTerm
.
正如我的假设。你也可以这样试试
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<div className="item
元素下方。当 div 元素被滚动条触发时,fetchData() 一次又一次地调用。
说明:
- 在这种情况下我们需要使用IntersectionObserver for read the element position. we need to pass two parameter for IntersectionObserver。
-首先在 hanlderObserver 中您可以看到 boundingClientRect.y. the boundingClientRect 方法读取元素位置。在这种情况下,我们只需要 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>
);
};
我希望它是正确的,我不确定。可能对某人有帮助。谢谢..
我正在尝试制作一个类似于 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;
让我们检查一下这段代码
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()
在InboxList
组件中,这段代码表达的意思
if (isOnScreen) {
fetchData();
}
就是每次InboxList
渲染时,如果屏幕上有航路点,则启动抓取,不管之前的抓取是否仍在进行中。
请注意,由于多种原因,InboxList
可能会在获取过程中重新呈现,可能会多次呈现,例如父组件重新渲染。只要航路点在屏幕上,每次重新渲染都会启动新的获取。
为了防止这种情况,我们需要跟踪正在进行的提取,类似于典型的 isLoading
状态变量。然后仅在 isLoading === false && isOnScreen
.
或者,如果保证每次获取都会将路径点推离屏幕,那么我们可以仅在路径点即将出现在屏幕上时才启动获取,即isOnScreen
正在从 false
更改为 true
:
useEffect(() => {
if (isOnScreen) {
fetchData();
}
}, [isOnScreen]);
但是,如果我们的假设(航点在每次获取时都离开屏幕)不成立,则这将无法正常运行。这可能发生,因为
pageSize
的fetch small and display area可以容纳更多 元素- 由于以下原因,从提取中收到的数据被过滤掉
客户端过滤,例如
searchTerm
.
正如我的假设。你也可以这样试试
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<div className="item
元素下方。当 div 元素被滚动条触发时,fetchData() 一次又一次地调用。
说明:
- 在这种情况下我们需要使用IntersectionObserver for read the element position. we need to pass two parameter for IntersectionObserver。
-首先在 hanlderObserver 中您可以看到 boundingClientRect.y. the boundingClientRect 方法读取元素位置。在这种情况下,我们只需要 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> );
};
我希望它是正确的,我不确定。可能对某人有帮助。谢谢..