jaredLunde / jaredLunde/masonic
[Question] I have a bunch of use cases together that I want to implement -> grid inside a dialog + infinite scroll + filters
- Dominant language
- TypeScript
- Stars
- 1.4k
- Forks
- 63
- PR merge metrics
- No merged PRs in 30d
Description
Hey @jaredLunde
There's a bunch of things I'm trying to do together:
Now if I add allMedia to usePositioner deps, because of the Infinite scroll, the grid starts glitching,
I also wanted to have loaders at the end as shown in the itemsWithPlaceholders, now if i switch the tabs and the data changes it errors out and if i put allMedia in the deps, it keeps glitching
Also one issue related to the grid rendering, whenever i render it for the first time the height and width are 0 and then if I change the filter it renders!
Would appreciate a lot if you can help me out with this thanks!
For reference this is my code:
```import React, { type MutableRefObject, useCallback } from 'react';
import {
useInfiniteLoader,
useMasonry,
usePositioner,
useResizeObserver,
} from 'masonic';
import { SkeletonTile } from './components/Skeleton';
import { useScroller, useSize } from 'mini-virtual-list';
export const LOADER_ID_PREFIX = '__skeleton__';
export const GUTTER_SIZE = 3;
type GridProps = {
allMedia: T[];
renderItem: (mediaItem: T) => React.ReactNode;
maxColumnCount?: number;
loaderGridSize?: number;
isLoading?: boolean;
isFetchingMore?: boolean;
hasMore?: boolean;
resetGrid?: boolean;
onLoadMore?: () => Promise;
// Pass in a ref function to get the scroll container element
scrollContainer: MutableRefObject;
};
const Grid = ({
allMedia = [],
renderItem,
maxColumnCount = 5,
loaderGridSize = 9,
isLoading = false,
isFetchingMore = false,
hasMore = false,
onLoadMore,
scrollContainer,
}: GridProps) => {
const { scrollTop, isScrolling } = useScroller(scrollContainer);
const { width, height } = useSize(scrollContainer);
const positioner = usePositioner(
{
width,
columnGutter: GUTTER_SIZE,
rowGutter: GUTTER_SIZE,
maxColumnCount,
},
[],
);
const resizeObserver = useResizeObserver(positioner);
const itemsWithPlaceholders = useMemo(() => {
if (!isFetchingMore && !isLoading) return allMedia;
return [...allMedia, ...getPlaceHolderItems(loaderGridSize)];
}, [allMedia, isFetchingMore, isLoading, loaderGridSize]);
const loadMoreItems = useCallback(async () => {
if (!hasMore || !onLoadMore || isFetchingMore) return;
await onLoadMore();
}, [hasMore, onLoadMore, isFetchingMore]);
const isItemLoaded = useCallback(
(idx: number) => idx < allMedia.length,
[allMedia.length],
);
const maybeLoadMore = useInfiniteLoader(loadMoreItems, {
isItemLoaded,
threshold: Math.max(6, loaderGridSize),
});
const itemKey = useCallback((data: any, index: number) => {
return data?.id ?? `item-${index}`;
}, []);
const renderItems = useCallback(
({ data }: { data: T }) => {
if ((data as any)?.id?.startsWith(LOADER_ID_PREFIX)) {
return ;
}
return renderItem(data);
},
[renderItem],
);
return useMasonry({
id: 'dream-masonry-grid',
positioner,
resizeObserver,
items: itemsWithPlaceholders,
height,
scrollTop,
isScrolling,
overscanBy: 2,
itemKey,
itemHeightEstimate: 300,
render: renderItems,
onRender: maybeLoadMore,
});
};
export default DreamGrid;
```
Contributor guide
Research direction
Start with the provided Grid component and its usePositioner, useMasonry, useInfiniteLoader, useResizeObserver, useScroller, and useSize calls; reproduce the dialog, tab-switching, filtering, and infinite-scroll cases. Check the SkeletonTile and itemsWithPlaceholders path as well. Done means the grid renders with nonzero dimensions initially, tab or filter changes do not error, and infinite scrolling does not glitch.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100