I am writing a frontend in React and have a component that uses VirtuosoGrid()
to render a collection of images. As images are uploaded, new images appear at the bottom of the page. I would like to reverse this so that newer images are shown first, and older images are shown last (or, more specifically, images are shown in reverse order of their database ID -- I don't care about the last-modified date of the image, I care about it's database ID and when it was uploaded).
I found that VirtuosoGrid()
has a reversed
prop that accepts a boolean, but assigning this a value of "true" does nothing. Am I missing something to make this work?
The images are passed to this component as an array, in order of database IDs; is there a way in JavaScript to quickly reverse this array, and then I can pass that to the VirtuosoGrid()
component without having to alter it at all? Or, is there a way to write a map()
function that iterates backwards through the array?
My original code for the VirtuosoGrid()
(which started from this documentation page):
/**
* This module adapted from react-virtuoso docs
* https://virtuoso.dev/grid-responsive-columns/
*/
import React, { forwardRef, useContext, useEffect, useState } from 'react';
import { VirtuosoGrid } from 'react-virtuoso';
import { useTheme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import Thumbnail from './Thumbnail';
import AppDataContext from './SupportingModules/AppDataContext';
export default function VirtuosoGridWrapper (props) {
const theme = useTheme();
const smallScreen = useMediaQuery(theme.breakpoints.down('sm'));
const mediumScreen = useMediaQuery(theme.breakpoints.only('md'));
const largeScreen = useMediaQuery(theme.breakpoints.only('lg'));
const hugeScreen = useMediaQuery(theme.breakpoints.only('xl'));
const [itemWidth, setItemWidth] = useState('33%');
const handleResize = () => {
if (smallScreen) {
setItemWidth('33%'); // 100% / 3 columns = 33%
} else if (mediumScreen) {
setItemWidth('25%'); // 100% / 4 columns = 25%
} else if (largeScreen) {
setItemWidth('20%'); // 100% / 5 columns = 20%
} else if (hugeScreen) {
setItemWidth('14.28%'); // 100% / 7 columns = ~14.28%
} else {
setItemWidth('33%'); // If unclear, default to small size
};
};
useEffect(() => {handleResize();}, [smallScreen, mediumScreen, largeScreen, hugeScreen])
// Ensure that this stays out of the component,
// Otherwise the grid will remount with each render due to new component instances.
const gridComponents = {
List: forwardRef(({ style, children, ...props }, ref) => (
<div
ref={ref}
{...props}
style={{
display: "flex",
flexWrap: "wrap",
...style,
}}
>
{children}
</div>
)),
Item: ({ children, ...props }) => (
<div
{...props}
style={{
padding: "0",
width: itemWidth,
display: "flex",
flex: "none",
alignContent: "stretch",
boxSizing: "border-box",
}}
>
{children}
</div>
)
}
const ItemWrapper = ({ children, ...props }) => (
<div
{...props}
style={{
display: "flex",
flex: 1,
textAlign: "center",
padding: "0.5rem 0.5rem",
border: "0px dashed red",
whiteSpace: "nowrap",
}}
>
{children}
</div>
);
function VirtualizedImageList() {
const {appData} = useContext(AppDataContext);
const imageList = appData.imageData;
return (
<VirtuosoGrid
style={{ height: "100vh" }}
totalCount={imageList.length}
components={gridComponents}
itemContent={(index) =>
<ItemWrapper>
<Thumbnail src={imageList[index].source} id={imageList[index].id}/>
</ItemWrapper>
}
/>
);
};
return <VirtualizedImageList imageList={props.imageList}/>;
};
I have tried this, to no effect:
<VirtuosoGrid
style={{ height: "100vh" }}
totalCount={imageList.length}
components={gridComponents}
reversed={true} // "reversed" prop is not working
itemContent={(index) =>
<ItemWrapper>
<Thumbnail src={imageList[index].source} id={imageList[index].id}/>
</ItemWrapper>
}
/>
A solution I found after some fiddling:
<VirtuosoGrid
style={{ height: "100vh" }}
totalCount={imageList.length}
components={gridComponents}
reversed={true}
itemContent={(index) =>
<ItemWrapper>
<Thumbnail
// Provide images in reverse order -- (length - index)
src={imageList[(imageList.length-1)-index].source}
id={imageList[(imageList.length-1)-index].id}/>
</ItemWrapper>
}
/>
This causes the images to be accessed in reverse order and creates the desired effect. Perhaps not the most elegant solution, but it does work.
If anyone has a better option, I am open to improvements on this!