Virtualize item list scrolling
This helps the render time by a lot!
This commit is contained in:
parent
cc4e1f611f
commit
be3a162a8a
3 changed files with 113 additions and 11 deletions
|
@ -49,6 +49,7 @@
|
||||||
"react-router-hash-link": "^2.4.3",
|
"react-router-hash-link": "^2.4.3",
|
||||||
"react-scripts": "^4.0.1",
|
"react-scripts": "^4.0.1",
|
||||||
"react-transition-group": "^4.3.0",
|
"react-transition-group": "^4.3.0",
|
||||||
|
"react-virtualized": "^9.22.3",
|
||||||
"simple-markdown": "^0.7.2",
|
"simple-markdown": "^0.7.2",
|
||||||
"tweenjs": "^1.0.2",
|
"tweenjs": "^1.0.2",
|
||||||
"typescript": "^4.1.3",
|
"typescript": "^4.1.3",
|
||||||
|
|
|
@ -25,6 +25,11 @@ import {
|
||||||
import { gql, useMutation, useQuery } from "@apollo/client";
|
import { gql, useMutation, useQuery } from "@apollo/client";
|
||||||
import { Link, useParams } from "react-router-dom";
|
import { Link, useParams } from "react-router-dom";
|
||||||
import { HashLink } from "react-router-hash-link";
|
import { HashLink } from "react-router-hash-link";
|
||||||
|
import {
|
||||||
|
List as VirtualizedList,
|
||||||
|
AutoSizer,
|
||||||
|
WindowScroller,
|
||||||
|
} from "react-virtualized";
|
||||||
|
|
||||||
import { Heading1, Heading3, MajorErrorMessage, usePageTitle } from "./util";
|
import { Heading1, Heading3, MajorErrorMessage, usePageTitle } from "./util";
|
||||||
import HangerSpinner from "./components/HangerSpinner";
|
import HangerSpinner from "./components/HangerSpinner";
|
||||||
|
@ -399,17 +404,10 @@ export function ClosetListContents({
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{itemsToShow.length > 0 ? (
|
{itemsToShow.length > 0 ? (
|
||||||
<Wrap spacing="4" justify="center">
|
<ClosetItemList
|
||||||
{itemsToShow.map((item) => (
|
items={itemsToShow}
|
||||||
<WrapItem key={item.id}>
|
|
||||||
<ItemCard
|
|
||||||
item={item}
|
|
||||||
variant="grid"
|
|
||||||
tradeMatchingMode={tradeMatchingMode}
|
tradeMatchingMode={tradeMatchingMode}
|
||||||
/>
|
/>
|
||||||
</WrapItem>
|
|
||||||
))}
|
|
||||||
</Wrap>
|
|
||||||
) : (
|
) : (
|
||||||
<Box fontStyle="italic">This list is empty!</Box>
|
<Box fontStyle="italic">This list is empty!</Box>
|
||||||
)}
|
)}
|
||||||
|
@ -442,6 +440,79 @@ export function ClosetListContents({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK: Measured by hand from <SquareItemCard />, plus 16px padding.
|
||||||
|
const ITEM_CARD_WIDTH = 112 + 16;
|
||||||
|
const ITEM_CARD_HEIGHT = 171 + 16;
|
||||||
|
|
||||||
|
function ClosetItemList({ items, tradeMatchingMode }) {
|
||||||
|
const renderItem = (item) => (
|
||||||
|
<ItemCard
|
||||||
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
variant="grid"
|
||||||
|
tradeMatchingMode={tradeMatchingMode}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
// For small lists, we don't bother to virtualize, because it slows down
|
||||||
|
// scrolling! (This helps a lot on the lists index page.)
|
||||||
|
if (items.length < 30) {
|
||||||
|
return (
|
||||||
|
<Wrap spacing="4" justify="center">
|
||||||
|
{items.map((item) => (
|
||||||
|
<WrapItem key={item.id}>{renderItem(item)}</WrapItem>
|
||||||
|
))}
|
||||||
|
</Wrap>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WindowScroller>
|
||||||
|
{({ height, isScrolling, onChildScroll, scrollTop, registerChild }) => (
|
||||||
|
<AutoSizer disableHeight>
|
||||||
|
{({ width }) => {
|
||||||
|
const numItemsPerRow = Math.floor(width / ITEM_CARD_WIDTH);
|
||||||
|
const numRows = Math.ceil(items.length / numItemsPerRow);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={registerChild}>
|
||||||
|
<VirtualizedList
|
||||||
|
autoHeight
|
||||||
|
height={height}
|
||||||
|
width={width}
|
||||||
|
rowCount={numRows}
|
||||||
|
rowHeight={ITEM_CARD_HEIGHT}
|
||||||
|
rowRenderer={({ index: rowIndex, key, style }) => {
|
||||||
|
const firstItemIndex = rowIndex * numItemsPerRow;
|
||||||
|
const itemsForRow = items.slice(
|
||||||
|
firstItemIndex,
|
||||||
|
firstItemIndex + numItemsPerRow
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
key={key}
|
||||||
|
style={style}
|
||||||
|
spacing="4"
|
||||||
|
justify="center"
|
||||||
|
>
|
||||||
|
{itemsForRow.map(renderItem)}
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
isScrolling={isScrolling}
|
||||||
|
onScroll={onChildScroll}
|
||||||
|
scrollTop={scrollTop}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</AutoSizer>
|
||||||
|
)}
|
||||||
|
</WindowScroller>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function buildClosetListPath(closetList) {
|
export function buildClosetListPath(closetList) {
|
||||||
let ownsOrWants;
|
let ownsOrWants;
|
||||||
if (closetList.ownsOrWantsItems === "OWNS") {
|
if (closetList.ownsOrWantsItems === "OWNS") {
|
||||||
|
|
30
yarn.lock
30
yarn.lock
|
@ -7884,6 +7884,11 @@ clone-response@^1.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
mimic-response "^1.0.0"
|
mimic-response "^1.0.0"
|
||||||
|
|
||||||
|
clsx@^1.0.4:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
||||||
|
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
||||||
|
|
||||||
co@^4.6.0:
|
co@^4.6.0:
|
||||||
version "4.6.0"
|
version "4.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||||
|
@ -9091,6 +9096,14 @@ dom-helpers@^5.0.1:
|
||||||
"@babel/runtime" "^7.8.7"
|
"@babel/runtime" "^7.8.7"
|
||||||
csstype "^2.6.7"
|
csstype "^2.6.7"
|
||||||
|
|
||||||
|
dom-helpers@^5.1.3:
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
|
||||||
|
integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.8.7"
|
||||||
|
csstype "^3.0.2"
|
||||||
|
|
||||||
dom-serializer@0:
|
dom-serializer@0:
|
||||||
version "0.2.2"
|
version "0.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
|
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
|
||||||
|
@ -15993,6 +16006,11 @@ react-is@^17.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
|
||||||
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
|
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
|
||||||
|
|
||||||
|
react-lifecycles-compat@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||||
|
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||||
|
|
||||||
react-refresh@^0.8.3:
|
react-refresh@^0.8.3:
|
||||||
version "0.8.3"
|
version "0.8.3"
|
||||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
|
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
|
||||||
|
@ -16145,6 +16163,18 @@ react-transition-group@^4.3.0:
|
||||||
loose-envify "^1.4.0"
|
loose-envify "^1.4.0"
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
|
react-virtualized@^9.22.3:
|
||||||
|
version "9.22.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.22.3.tgz#f430f16beb0a42db420dbd4d340403c0de334421"
|
||||||
|
integrity sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.7.2"
|
||||||
|
clsx "^1.0.4"
|
||||||
|
dom-helpers "^5.1.3"
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
react-lifecycles-compat "^3.0.4"
|
||||||
|
|
||||||
react@^17.0.1:
|
react@^17.0.1:
|
||||||
version "17.0.1"
|
version "17.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
|
||||||
|
|
Loading…
Reference in a new issue