![](/img/trans.png)
[英]React Infinite Scroll - Intersection Observer API always jumping back to top
[英]Intersection Observer API going into infinite rendering loop
當用戶開始滾動時,我正在嘗試使用交叉點觀察器 API 有條件地在 CSS 網格中顯示項目,但似乎 go 進入無限渲染循環。 這是我的代碼。
這是我在 StackBlitz 上的實時代碼的鏈接
另外,我要實現的目標是在可以避免的情況下不要在屏幕上渲染太多項目。 我不確定是否display: none
實際上會使瀏覽器工作得更少。 如果這不是正確的方法,請告訴我。
感謝您閱讀我的問題。 非常感謝任何幫助。
該組件未按您預期的方式工作,因為您對所有項目使用相同的引用。 您可以使用ref
存儲引用數組或使用列表項邏輯創建組件。
如果不想同時渲染所有項目,可以渲染一部分(100),每次scroll
到末尾,再渲染100,以此類推。 我建議你使用React.memo
來避免每次 state 更新時渲染項目:
PortfolioItem.js
const PortfolioItem = React.memo(({ ix }) => {
const ref = useRef();
const [inViewRef, inView] = useInView({
threshold: 1,
rootMargin: '0px',
});
const setRefs = useCallback(
(node) => {
ref.current = node;
inViewRef(node);
},
[], //--> empty dependencies
);
return ( <GridItem bg={inView?"red.100":"blue.100"} ref={setRefs} _last={{ mb: 4 }} >
<Center border={1} borderColor="gray.100" borderStyle="solid" h={16} w="100%">
Item {ix}
</Center>
</GridItem>)
});
投資組合列表.js
export const PortfolioList = ({
title,
count = 100
}: PortfolioListProps) => {
const ref = useRef(null);
const items = [...Array(1000)];
const [index, setIndex] = useState(count);
useEffect(()=> {
const grid = ref.current;
function onScroll(){
if(grid.offsetHeight + grid.scrollTop >= grid.scrollHeight) {
setIndex(prev => prev+count);
}
}
grid.addEventListener("scroll", onScroll);
return ()=> {
grid.removeEventListener("scroll", onScroll);
}
}, []);
return (
<Box
w="100%"
mx="auto"
rounded={{ md: `lg` }}
bg={mode(`white`, `gray.700`)}
shadow="md"
overflow="hidden"
>
<Flex align="center" justify="space-between" px="6" py="4">
<Text as="h3" fontWeight="bold" fontSize="xl">
{title}
</Text>
</Flex>
<Divider />
<Grid
p={4}
gap={4}
templateColumns="1fr 1fr 1fr 1fr"
templateRows="min-content"
maxH="500px"
minH="500px"
overflowY="auto"
id="list"
ref={ref}
>
{items.slice(0,index).map((pt,ix) => (
<PortfolioItem ix={ix} key={`Postfolio__item-${ix}`}/>
))
}
</Grid>
</Box>
);
};
您有 1000 個GridItem
組件,它們都獲得相同的回調 ref setRefs
。 盡管我們知道在任何給定時間有些在視圖中而有些不在視圖中,但它們都收到相同的inView
值。 最終發生的是每個項目都會覆蓋先前設置的 ref,這樣所有 1000 個項目都會收到一個 boolean inView
,它表示列表中的最后一個項目是否在視圖中——而不是它本身是否在視圖中。
useInView
為了知道每個單獨的組件是否在視圖中,我們需要對列表中的每個元素分別使用useInView
鈎子。 我們可以將每個項目的代碼移動到它自己的組件中。 我們需要傳遞這個組件的編號ix
和useInView
掛鈎的選項(我們也可以只傳遞根 ref 並在此處創建options
object)。
import { Box, Flex, Text, useColorModeValue as mode, Divider, Grid, GridItem, Center } from '@chakra-ui/react';
import { useInView, IntersectionOptions } from 'react-intersection-observer';
import React, { useRef } from 'react';
interface ItemProps {
ix: number;
inViewOptions: IntersectionOptions;
}
export const ListItem = ({ix, inViewOptions}: ItemProps) => {
const {ref, inView}= useInView(inViewOptions);
return (
<GridItem bg={inView?"red.100":"blue.100"} ref={ref} _last={{ mb: 4 }} key={ix}>
<Center border={1} borderColor="gray.100" borderStyle="solid" h={16} w="100%">
Item {ix}
</Center>
</GridItem>
)
}
export type PortfolioListProps = {
title: string;
};
export const PortfolioList = ({
title,
}: PortfolioListProps) => {
const parRef = useRef(null);
return (
<Box
w="100%"
mx="auto"
rounded={{ md: `lg` }}
bg={mode(`white`, `gray.700`)}
shadow="md"
overflow="hidden"
>
<Flex align="center" justify="space-between" px="6" py="4">
<Text as="h3" fontWeight="bold" fontSize="xl">
{title}
</Text>
</Flex>
<Divider />
<Grid
p={4}
gap={4}
templateColumns="1fr 1fr 1fr 1fr"
templateRows="min-content"
maxH="500px"
minH="500px"
overflowY="auto"
id="list"
ref={parRef}
>
{[...Array(1000)].map((pt,ix) => (
<ListItem ix={ix} key={ix} inViewOptions={{
threshold: 1,
rootMargin: '0px',
root: parRef?.current,
}}/>
))}
</Grid>
</Box>
);
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.