为什么我必须强制重新渲染才能显示任何内容?

问题描述 投票:0回答:1

信息

我正在尝试通过在反应中利用虚拟化来实现无限滚动。为此,我使用包

@tanstack/react-query
@tanstack/react-virtual

问题

我已经设法让它工作,但我必须强制重新渲染才能显示任何内容。我已将问题范围缩小到初始化时

scrollRef
选项所需的
useVirtualizer
为了让我的应用程序的布局正常工作,我必须将获取scrollRef的容器移动到父组件,并在其中设置

getScrollElement

以作为道具传递给子组件,以便

useRef
可以使用并设置
useVirtualizer
如果我将带有scrollRef和

ref.current

的容器移动到调用

useRef
的子组件中,它可以工作而无需强制重新渲染,但我的应用程序中的布局将无法正常工作,因为它是一个电子应用程序使用“自定义”滚动条而不是窗口滚动条。
下面是我的代码的简化版本。我还创建了一个 CodeSandbox,您可以在其中查看实际行为。

CodeSandbox 链接

(点击强制重新渲染按钮即可显示内容)

代码

useVirtualizer


reactjs
1个回答
0
投票

如果您使用 useState 而不是使用 useRef 将 ref 存储在状态中,则它可以正常工作。

const Shows = ({ scrollRef, }: { scrollRef: React.RefObject<HTMLDivElement>; }) => { const [, setForceRerender] = useState(0); const { data, isFetchingNextPage, fetchNextPage, hasNextPage } = useSuspenseInfiniteQuery({ queryKey: ["shows"], queryFn: async ({ pageParam = 1 }) => { const { data } = await axios.get( "https://demoapi.com?page=" + pageParam, ); return data; }, initialPageParam: 1, getNextPageParam: (lastPage) => lastPage.page < lastPage.total_pages ? lastPage.page + 1 : undefined, }); const shows = data ? data.pages.flatMap((page) => page.results) : []; const columnCount = 5; const rowCount = Math.ceil(shows.length / columnCount); const itemWidth = 166; const itemHeight = 248; const rowVirtualizer = useVirtualizer({ count: hasNextPage ? rowCount + 1 : rowCount, getScrollElement: () => scrollRef.current, estimateSize: () => itemHeight, overscan: 1, }); const columnVirtualizer = useVirtualizer({ horizontal: true, count: columnCount, getScrollElement: () => scrollRef.current, estimateSize: () => itemWidth, overscan: 0, }); const rowItems = rowVirtualizer.getVirtualItems(); useEffect(() => { const [lastItem] = [...rowItems].reverse(); if (!lastItem) { return; } if ( (lastItem.index + 1) * columnCount >= shows.length - 1 && hasNextPage && !isFetchingNextPage ) { fetchNextPage(); } }, [hasNextPage, fetchNextPage, shows.length, isFetchingNextPage, rowItems]); return ( <div style={{ width: "100%" }}> <button onClick={() => setForceRerender((prev) => prev + 1)}> Force rerender </button> <div style={{ height: `${rowVirtualizer.getTotalSize()}px`, position: "relative", width: "100%", userSelect: "none", }} > {rowVirtualizer.getVirtualItems().map((virtualRow) => columnVirtualizer.getVirtualItems().map((virtualColumn) => { const isLoaderRow = virtualRow.index > Math.ceil(shows.length / 5) - 1; const index = virtualColumn.index + virtualRow.index * columnCount; const show = shows[index]; return ( <div key={index} style={{ position: "absolute", top: 0, left: 0, width: `${virtualColumn.size}px`, height: `${virtualRow.size}px`, transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`, }} > {!isLoaderRow && show && ( <div key={index} style={{ cursor: "pointer" }}> <img style={{ height: "232px", width: "150px", objectFit: "fill", }} src={`${ show.poster_path ? `https://image.tmdb.org/t/p/w185/${show.poster_path}` : "https://via.placeholder.com/185x278?text=Image+missing" }`} alt="" /> </div> )} </div> ); }), )} </div> {shows.length === 0 && ( <div className="flex h-full w-full items-center justify-center"> No shows to discover that matches your filters </div> )} {!hasNextPage && shows.length > 0 && ( <div className="mt-2 flex justify-center"> No more shows to discover </div> )} {isFetchingNextPage && ( <div className="flex items-center">Loading next page...</div> )} </div> ); }; function App() { const scrollRef = useRef<HTMLDivElement>(null); return ( <Suspense fallback={<div>Loading in app.tsx...</div>}> <div style={{ height: "500px", overflow: "auto", }} ref={scrollRef} > <Shows scrollRef={scrollRef} /> </div> </Suspense> ); }

然后将 ref 的用途从 
const [scrollRef, setScrollRef] = useState<HTMLDivElement | null>(null); <div ref={scrollRef}>...</div>

更改为

scrollRef.current
    

© www.soinside.com 2019 - 2024. All rights reserved.