From 2a14d8a41dc477463228667271000bd62458ec9a Mon Sep 17 00:00:00 2001 From: Benjamin Cooke Date: Fri, 3 Mar 2023 15:11:33 -0500 Subject: [PATCH] making the table a virtual list to improve performance --- webapp/package-lock.json | 83 +++++++++++++++++++++++ webapp/package.json | 4 ++ webapp/src/components/table/table.scss | 6 +- webapp/src/components/table/tableRows.tsx | 50 +++++++++++--- 4 files changed, 133 insertions(+), 10 deletions(-) diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 65f21b94b..e7d0d95b0 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -48,6 +48,8 @@ "react-redux": "^7.2.0", "react-router-dom": "^5.2.0", "react-select": "^5.2.2", + "react-virtualized-auto-sizer": "^1.0.7", + "react-window": "^1.8.8", "trim-newlines": "^4.0.2" }, "devDependencies": { @@ -72,6 +74,8 @@ "@types/react-redux": "^7.1.23", "@types/react-router-dom": "^5.3.3", "@types/react-select": "^5.0.0", + "@types/react-virtualized-auto-sizer": "^1.0.1", + "@types/react-window": "^1.8.5", "@types/redux-mock-store": "^1.0.3", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", @@ -2795,6 +2799,24 @@ "@types/react": "*" } }, + "node_modules/@types/react-virtualized-auto-sizer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.1.tgz", + "integrity": "sha512-GH8sAnBEM5GV9LTeiz56r4ZhMOUSrP43tAQNSRVxNexDjcNKLCEtnxusAItg1owFUFE6k0NslV26gqVClVvong==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-window": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.5.tgz", + "integrity": "sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/redux-mock-store": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/redux-mock-store/-/redux-mock-store-1.0.3.tgz", @@ -14075,6 +14097,34 @@ "react-dom": ">=16.6.0" } }, + "node_modules/react-virtualized-auto-sizer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.7.tgz", + "integrity": "sha512-Mxi6lwOmjwIjC1X4gABXMJcKHsOo0xWl3E3ugOgufB8GJU+MqrtY35aBuvCYv/razQ1Vbp7h1gWJjGjoNN5pmA==", + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc", + "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc" + } + }, + "node_modules/react-window": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.8.tgz", + "integrity": "sha512-D4IiBeRtGXziZ1n0XklnFGu7h9gU684zepqyKzgPNzrsrk7xOCxni+TCckjg2Nr/DiaEEGVVmnhYSlT2rB47dQ==", + "dependencies": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + }, + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -19590,6 +19640,24 @@ "@types/react": "*" } }, + "@types/react-virtualized-auto-sizer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.1.tgz", + "integrity": "sha512-GH8sAnBEM5GV9LTeiz56r4ZhMOUSrP43tAQNSRVxNexDjcNKLCEtnxusAItg1owFUFE6k0NslV26gqVClVvong==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-window": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.5.tgz", + "integrity": "sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/redux-mock-store": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/redux-mock-store/-/redux-mock-store-1.0.3.tgz", @@ -28118,6 +28186,21 @@ "prop-types": "^15.6.2" } }, + "react-virtualized-auto-sizer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.7.tgz", + "integrity": "sha512-Mxi6lwOmjwIjC1X4gABXMJcKHsOo0xWl3E3ugOgufB8GJU+MqrtY35aBuvCYv/razQ1Vbp7h1gWJjGjoNN5pmA==", + "requires": {} + }, + "react-window": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.8.tgz", + "integrity": "sha512-D4IiBeRtGXziZ1n0XklnFGu7h9gU684zepqyKzgPNzrsrk7xOCxni+TCckjg2Nr/DiaEEGVVmnhYSlT2rB47dQ==", + "requires": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + } + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", diff --git a/webapp/package.json b/webapp/package.json index d26d1e3ae..4b0568bd7 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -65,6 +65,8 @@ "react-redux": "^7.2.0", "react-router-dom": "^5.2.0", "react-select": "^5.2.2", + "react-virtualized-auto-sizer": "^1.0.7", + "react-window": "^1.8.8", "trim-newlines": "^4.0.2" }, "jest": { @@ -113,6 +115,8 @@ "@types/react-redux": "^7.1.23", "@types/react-router-dom": "^5.3.3", "@types/react-select": "^5.0.0", + "@types/react-virtualized-auto-sizer": "^1.0.1", + "@types/react-window": "^1.8.5", "@types/redux-mock-store": "^1.0.3", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", diff --git a/webapp/src/components/table/table.scss b/webapp/src/components/table/table.scss index bbc9ef118..30b67fa1b 100644 --- a/webapp/src/components/table/table.scss +++ b/webapp/src/components/table/table.scss @@ -212,7 +212,8 @@ .octo-table-body { display: flex; flex-direction: column; - width: fit-content; + width: 100%; + height: 100%; } .octo-table-header, @@ -250,7 +251,8 @@ } .table-row-container { - width: fit-content; + width: 100%; + height: 100%; .octo-table-cell { align-items: center; diff --git a/webapp/src/components/table/tableRows.tsx b/webapp/src/components/table/tableRows.tsx index c0460d970..ee97782e0 100644 --- a/webapp/src/components/table/tableRows.tsx +++ b/webapp/src/components/table/tableRows.tsx @@ -1,6 +1,8 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import React, {useCallback} from 'react' +import {FixedSizeList, ListChildComponentProps} from 'react-window'; +import AutoSizer from 'react-virtualized-auto-sizer'; import {Card} from '../../blocks/card' import {Board} from '../../blocks/board' @@ -26,14 +28,26 @@ type Props = { const TableRows = (props: Props): JSX.Element => { const {board, cards, activeView} = props + console.log(cards); + const onClickRow = useCallback((e: React.MouseEvent, card: Card) => { props.onCardClicked(e, card) }, [props.onCardClicked]) - return ( - <> - {cards.map((card, idx) => { - return ( + const isItemLoaded = (index: number) => { + return index < cards.length; + }; + + const Item = ({index, style}: ListChildComponentProps) => { + + const card = cards[index] + console.log(card) + if (isItemLoaded(index)) { + return ( +
{ addCard={props.addCard} isSelected={props.selectedCardIds.includes(card.id)} focusOnMount={props.cardIdToFocusOnRender === card.id} - isLastCard={idx === (cards.length - 1)} + isLastCard={index === (cards.length - 1)} onClick={onClickRow} showCard={props.showCard} readonly={props.readonly} onDrop={props.onDrop} - />) - })} - + /> +
+ + ) + } + + return null + } + + return ( + + {({height, width}) => ( + + {Item} + + )} + + ) }