Improve virtual grid performance

This commit is contained in:
jeffvli
2022-12-09 15:58:30 -08:00
parent 9f3c6d3029
commit e32ade3b54
6 changed files with 91 additions and 81 deletions
@@ -7,7 +7,7 @@ import { SimpleImg } from 'react-simple-img';
import type { ListChildComponentProps } from 'react-window';
import styled from 'styled-components';
import { Text } from '/@/components/text';
import type { PlayQueueAddOptions, LibraryItem, CardRow, CardRoute, Play } from '/@/types';
import type { LibraryItem, CardRow, CardRoute, Play } from '/@/types';
import GridCardControls from './grid-card-controls';
const CardWrapper = styled.div<{
@@ -113,7 +113,6 @@ interface BaseGridCardProps {
controls: {
cardControls: any[];
cardRows: CardRow[];
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
itemType: LibraryItem;
playButtonBehavior: Play;
route: CardRoute;
@@ -135,7 +134,7 @@ export const DefaultCard = ({
sizes,
}: BaseGridCardProps) => {
const navigate = useNavigate();
const { index, isScrolling } = listChildProps;
const { index } = listChildProps;
const { itemGap, itemHeight, itemWidth } = sizes;
const { itemType, cardRows, route } = controls;
@@ -190,12 +189,10 @@ export const DefaultCard = ({
</Center>
)}
<ControlsContainer>
{!isScrolling && (
<GridCardControls
itemData={data}
itemType={itemType}
/>
)}
<GridCardControls
itemData={data}
itemType={itemType}
/>
</ControlsContainer>
</ImageSection>
<DetailSection>
@@ -42,9 +42,18 @@ const PlayButton = styled.button<PlayButtonType>`
`;
const SecondaryButton = styled(_Button)`
fill: white !important;
svg: {
fill: white !important;
opacity: 0.8;
transition: opacity 0.2s ease-in-out;
transition: scale 0.2s linear;
&:hover {
opacity: 1;
scale: 1.1;
}
&:active {
opacity: 1;
scale: 1;
}
`;
@@ -132,13 +141,7 @@ export const GridCardControls = ({
{/* <TopControls /> */}
{/* <CenterControls /> */}
<BottomControls>
<PlayButton
// initial="initial"
// variants={buttonVariants}
// whileHover="hover"
// whileTap="pressed"
onClick={handlePlay}
>
<PlayButton onClick={handlePlay}>
<RiPlayFill size={25} />
</PlayButton>
<Group spacing="xs">
@@ -166,6 +169,7 @@ export const GridCardControls = ({
<DropdownMenu.Target>
<SecondaryButton
p={5}
sx={{ svg: { fill: 'white !important' } }}
variant="subtle"
onClick={(e) => {
e.preventDefault();
@@ -1,11 +1,12 @@
import { useMemo } from 'react';
import { memo } from 'react';
import type { ListChildComponentProps } from 'react-window';
import { areEqual } from 'react-window';
import { DefaultCard } from '/@/components/virtual-grid/grid-card/default-card';
import { PosterCard } from '/@/components/virtual-grid/grid-card/poster-card';
import type { GridCardData } from '/@/types';
import { CardDisplayType } from '/@/types';
export const GridCard = ({ data, index, style, isScrolling }: ListChildComponentProps) => {
export const GridCard = memo(({ data, index, style }: ListChildComponentProps) => {
const {
itemHeight,
itemWidth,
@@ -13,7 +14,6 @@ export const GridCard = ({ data, index, style, isScrolling }: ListChildComponent
itemGap,
itemCount,
cardControls,
handlePlayQueueAdd,
cardRows,
itemData,
itemType,
@@ -22,11 +22,8 @@ export const GridCard = ({ data, index, style, isScrolling }: ListChildComponent
display,
} = data as GridCardData;
const cards = [];
const startIndex = useMemo(() => index * columnCount, [columnCount, index]);
const stopIndex = useMemo(
() => Math.min(itemCount - 1, startIndex + columnCount - 1),
[columnCount, itemCount, startIndex],
);
const startIndex = index * columnCount;
const stopIndex = Math.min(itemCount - 1, startIndex + columnCount - 1);
const View = display === CardDisplayType.CARD ? DefaultCard : PosterCard;
@@ -38,28 +35,29 @@ export const GridCard = ({ data, index, style, isScrolling }: ListChildComponent
controls={{
cardControls,
cardRows,
handlePlayQueueAdd,
itemType,
playButtonBehavior,
route,
}}
data={itemData[i]}
listChildProps={{ index, isScrolling }}
listChildProps={{ index }}
sizes={{ itemGap, itemHeight, itemWidth }}
/>,
);
}
return (
<div
style={{
...style,
alignItems: 'center',
display: 'flex',
justifyContent: 'start',
}}
>
{cards}
</div>
<>
<div
style={{
...style,
alignItems: 'center',
display: 'flex',
justifyContent: 'start',
}}
>
{cards}
</div>
</>
);
};
}, areEqual);
@@ -8,7 +8,7 @@ import type { ListChildComponentProps } from 'react-window';
import styled from 'styled-components';
import { Skeleton } from '/@/components/skeleton';
import { Text } from '/@/components/text';
import type { PlayQueueAddOptions, LibraryItem, CardRow, CardRoute, Play } from '/@/types';
import type { LibraryItem, CardRow, CardRoute, Play } from '/@/types';
import GridCardControls from './grid-card-controls';
const CardWrapper = styled.div<{
@@ -117,7 +117,6 @@ interface BaseGridCardProps {
columnIndex: number;
controls: {
cardRows: CardRow[];
handlePlayQueueAdd: (options: PlayQueueAddOptions) => void;
itemType: LibraryItem;
playButtonBehavior: Play;
route: CardRoute;
@@ -166,7 +165,6 @@ export const PosterCard = ({
height={sizes.itemWidth}
importance="auto"
placeholder={'var(--card-default-bg)'}
// placeholder={data?.imagePlaceholderUrl || 'var(--card-default-bg)'}
src={data?.imageUrl}
width={sizes.itemWidth}
/>
@@ -184,14 +182,12 @@ export const PosterCard = ({
/>
</Center>
)}
{!listChildProps.isScrolling && (
<ControlsContainer>
<GridCardControls
itemData={data}
itemType={controls.itemType}
/>
</ControlsContainer>
)}
<ControlsContainer>
<GridCardControls
itemData={data}
itemType={controls.itemType}
/>
</ControlsContainer>
</ImageSection>
</Link>
<DetailSection>
@@ -1,11 +1,40 @@
import type { Ref } from 'react';
import { useMemo } from 'react';
import debounce from 'lodash/debounce';
import memoize from 'memoize-one';
import type { FixedSizeListProps } from 'react-window';
import { FixedSizeList } from 'react-window';
import styled from 'styled-components';
import { GridCard } from '/@/components/virtual-grid/grid-card';
import type { CardRow, LibraryItem, CardDisplayType, CardRoute } from '/@/types';
const createItemData = memoize(
(
cardRows,
columnCount,
display,
itemCount,
itemData,
itemGap,
itemHeight,
itemType,
itemWidth,
route,
) => ({
cardRows,
columnCount,
display,
itemCount,
itemData,
itemGap,
itemHeight,
itemType,
itemWidth,
route,
}),
);
const createScrollHandler = memoize((onScroll) => debounce(onScroll, 250));
export const VirtualGridWrapper = ({
refInstance,
cardRows,
@@ -35,44 +64,31 @@ export const VirtualGridWrapper = ({
route?: CardRoute;
rowCount: number;
}) => {
const memoizedItemData = useMemo(
() => ({
cardRows,
columnCount,
display,
itemCount,
itemData,
itemGap,
itemHeight,
itemType,
itemWidth,
route,
}),
[
cardRows,
itemType,
columnCount,
itemCount,
itemData,
display,
itemGap,
itemHeight,
route,
itemWidth,
],
const memoizedItemData = createItemData(
cardRows,
columnCount,
display,
itemCount,
itemData,
itemGap,
itemHeight,
itemType,
itemWidth,
route,
);
const memoizedOnScroll = createScrollHandler(onScroll);
return (
<FixedSizeList
ref={refInstance}
{...rest}
useIsScrolling
initialScrollOffset={initialScrollOffset}
itemCount={rowCount}
itemData={memoizedItemData}
itemSize={itemHeight}
overscanCount={5}
onScroll={onScroll}
onScroll={memoizedOnScroll}
>
{GridCard}
</FixedSizeList>
@@ -86,7 +86,7 @@ export const VirtualInfiniteGrid = ({
[columnCount, fetchFn, itemData],
);
const debouncedLoadMoreItems = debounce(loadMoreItems, 400);
const debouncedLoadMoreItems = debounce(loadMoreItems, 500);
useEffect(() => {
if (loader.current) {
@@ -110,7 +110,6 @@ export const VirtualInfiniteGrid = ({
>
{({ onItemsRendered, ref: infiniteLoaderRef }) => (
<VirtualGridWrapper
useIsScrolling
cardRows={cardRows}
columnCount={columnCount}
display={display || CardDisplayType.CARD}