From be3a162a8a7f865665b44e43b42769c76ef47f5e Mon Sep 17 00:00:00 2001 From: Matchu Date: Sat, 19 Jun 2021 12:36:19 -0700 Subject: [PATCH] Virtualize item list scrolling This helps the render time by a lot! --- package.json | 1 + src/app/UserItemListPage.js | 93 ++++++++++++++++++++++++++++++++----- yarn.lock | 30 ++++++++++++ 3 files changed, 113 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 6db4237..e6912a3 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "react-router-hash-link": "^2.4.3", "react-scripts": "^4.0.1", "react-transition-group": "^4.3.0", + "react-virtualized": "^9.22.3", "simple-markdown": "^0.7.2", "tweenjs": "^1.0.2", "typescript": "^4.1.3", diff --git a/src/app/UserItemListPage.js b/src/app/UserItemListPage.js index 37091cf..65cc6a4 100644 --- a/src/app/UserItemListPage.js +++ b/src/app/UserItemListPage.js @@ -25,6 +25,11 @@ import { import { gql, useMutation, useQuery } from "@apollo/client"; import { Link, useParams } from "react-router-dom"; import { HashLink } from "react-router-hash-link"; +import { + List as VirtualizedList, + AutoSizer, + WindowScroller, +} from "react-virtualized"; import { Heading1, Heading3, MajorErrorMessage, usePageTitle } from "./util"; import HangerSpinner from "./components/HangerSpinner"; @@ -399,17 +404,10 @@ export function ClosetListContents({ return ( {itemsToShow.length > 0 ? ( - - {itemsToShow.map((item) => ( - - - - ))} - + ) : ( This list is empty! )} @@ -442,6 +440,79 @@ export function ClosetListContents({ ); } +// HACK: Measured by hand from , plus 16px padding. +const ITEM_CARD_WIDTH = 112 + 16; +const ITEM_CARD_HEIGHT = 171 + 16; + +function ClosetItemList({ items, tradeMatchingMode }) { + const renderItem = (item) => ( + + ); + + // 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 ( + + {items.map((item) => ( + {renderItem(item)} + ))} + + ); + } + + return ( + + {({ height, isScrolling, onChildScroll, scrollTop, registerChild }) => ( + + {({ width }) => { + const numItemsPerRow = Math.floor(width / ITEM_CARD_WIDTH); + const numRows = Math.ceil(items.length / numItemsPerRow); + + return ( +
+ { + const firstItemIndex = rowIndex * numItemsPerRow; + const itemsForRow = items.slice( + firstItemIndex, + firstItemIndex + numItemsPerRow + ); + + return ( + + {itemsForRow.map(renderItem)} + + ); + }} + isScrolling={isScrolling} + onScroll={onChildScroll} + scrollTop={scrollTop} + /> +
+ ); + }} +
+ )} +
+ ); +} + export function buildClosetListPath(closetList) { let ownsOrWants; if (closetList.ownsOrWantsItems === "OWNS") { diff --git a/yarn.lock b/yarn.lock index 039e8b7..57298e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7884,6 +7884,11 @@ clone-response@^1.0.2: dependencies: 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: version "4.6.0" 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" 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: version "0.2.2" 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" 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: version "0.8.3" 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" 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: version "17.0.1" resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"