jaredLunde / jaredLunde/masonic

[Question] I have a bunch of use cases together that I want to implement -> grid inside a dialog + infinite scroll + filters

Open
#187 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.