Skip to content

Commit

Permalink
Merge pull request #671 from seungineer/main
Browse files Browse the repository at this point in the history
[view] 렌더링 성능 최적화를 위한 react-virtualized 라이브러리 도입
  • Loading branch information
seungineer authored Aug 27, 2024
2 parents f3546aa + 160ee86 commit 6ca5aeb
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 57 deletions.
41 changes: 41 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/view/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"@types/react-virtualized": "^9.21.30",
"classnames": "^2.3.2",
"d3": "^7.6.1",
"dayjs": "^1.11.5",
Expand All @@ -106,6 +107,7 @@
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-spinners": "^0.13.8",
"react-virtualized": "^9.22.5",
"reflect-metadata": "^0.1.13",
"svg-parser": "^2.0.4",
"ts-jest": "^29.1.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const Content = ({ content, clusterId, selectedClusterId }: ContentProps) => {
return acc;
}, []);
setLinkedStr(newLinkedStr);
}, []);
}, [content]);

return (
<>
Expand Down
147 changes: 91 additions & 56 deletions packages/view/src/components/VerticalClusterList/Summary/Summary.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useRef, useEffect } from "react";
import type { ListRowProps } from "react-virtualized";
import { List, AutoSizer } from "react-virtualized";

import type { ClusterNode } from "types";
import { Detail } from "components";
Expand All @@ -9,92 +11,125 @@ import { Author } from "../../@common/Author";
import { selectedDataUpdater } from "../VerticalClusterList.util";
import { ClusterGraph } from "../ClusterGraph";
import { getClusterSizes } from "../ClusterGraph/ClusterGraph.util";
import { CLUSTER_HEIGHT, DETAIL_HEIGHT, NODE_GAP } from "../ClusterGraph/ClusterGraph.const";

import { usePreLoadAuthorImg } from "./Summary.hook";
import { getInitData, getClusterIds, getClusterById } from "./Summary.util";
import { Content } from "./Content";
import type { Cluster } from "./Summary.type";

const COLLAPSED_ROW_HEIGHT = CLUSTER_HEIGHT + NODE_GAP * 2;
const EXPANDED_ROW_HEIGHT = DETAIL_HEIGHT + COLLAPSED_ROW_HEIGHT;

const Summary = () => {
const { filteredData: data, selectedData, setSelectedData } = useGlobalData();
const clusters = getInitData(data);
const detailRef = useRef<HTMLDivElement>(null);
const authSrcMap = usePreLoadAuthorImg();
const selectedClusterId = getClusterIds(selectedData);
const listRef = useRef<List>(null);
const clusterSizes = getClusterSizes(data);

const onClickClusterSummary = (clusterId: number) => () => {
const selected = getClusterById(data, clusterId);
setSelectedData((prevState: ClusterNode[]) => selectedDataUpdater(selected, clusterId)(prevState));
setSelectedData((prevState: ClusterNode[]) => {
return selectedDataUpdater(selected, clusterId)(prevState);
});
};

useEffect(() => {
detailRef.current?.scrollIntoView({
block: "center",
behavior: "smooth",
});
if (listRef.current) {
listRef.current.recomputeRowHeights();
}
}, [selectedData]);

return (
<div className="cluster-summary__container">
{clusters.map((cluster: Cluster, index: number) => {
return (
const getRowHeight = ({ index }: { index: number }) => {
const cluster = clusters[index];
return selectedClusterId.includes(cluster.clusterId) ? EXPANDED_ROW_HEIGHT : COLLAPSED_ROW_HEIGHT;
};

const rowRenderer = ({ index, key, style }: ListRowProps) => {
const cluster = clusters[index];
const isExpanded = selectedClusterId.includes(cluster.clusterId);

return (
<div
key={key}
style={style}
className={`cluster-summary__cluster ${isExpanded ? "expanded" : ""}`}
>
<div className="cluster-summary__graph-wrapper">
<ClusterGraph
data={[data[index]]}
clusterSizes={[clusterSizes[index]]}
/>
<div
role="presentation"
className="cluster-summary__cluster"
key={cluster.clusterId}
className={`cluster-summary__info-wrapper ${
selectedClusterId.includes(cluster.clusterId) ? "selected" : ""
}`}
/>
</div>
<div className="cluster-summary__info-wrapper">
<button
type="button"
className="toggle-contents-button"
onClick={onClickClusterSummary(cluster.clusterId)}
>
<div className="cluster-summary__graph-wrapper">
<ClusterGraph
data={[data[index]]}
clusterSizes={[clusterSizes[index]]}
<div className="toggle-contents-container">
<div className="name-box">
{authSrcMap &&
cluster.summary.authorNames.map((authorArray: string[]) => {
return authorArray.map((authorName: string) => (
<Author
key={authorName}
name={authorName}
src={authSrcMap[authorName]}
/>
));
})}
</div>
<Content
content={cluster.summary.content}
clusterId={cluster.clusterId}
selectedClusterId={selectedClusterId}
/>
</div>
</button>
{isExpanded && (
<div
className={`cluster-summary__info-wrapper ${
selectedClusterId.includes(cluster.clusterId) ? "selected" : ""
}`}
className="detail__container"
ref={detailRef}
>
<button
type="button"
className="toggle-contents-button"
onClick={onClickClusterSummary(cluster.clusterId)}
>
<div className="toggle-contents-container">
<div className="name-box">
{authSrcMap &&
cluster.summary.authorNames.map((authorArray: string[]) => {
return authorArray.map((authorName: string) => (
<Author
key={authorName}
name={authorName}
src={authSrcMap[authorName]}
/>
));
})}
</div>
<Content
content={cluster.summary.content}
clusterId={cluster.clusterId}
selectedClusterId={selectedClusterId}
/>
</div>
</button>
{selectedClusterId.includes(cluster.clusterId) && (
<div
className="detail__container"
ref={detailRef}
>
<Detail
selectedData={selectedData}
clusterId={cluster.clusterId}
authSrcMap={authSrcMap}
/>
</div>
)}
<Detail
selectedData={selectedData}
clusterId={cluster.clusterId}
authSrcMap={authSrcMap}
/>
</div>
</div>
);
})}
)}
</div>
</div>
);
};

return (
<div className="cluster-summary__container">
<AutoSizer>
{({ width, height }) => (
<List
ref={listRef}
width={width}
height={height}
rowCount={clusters.length}
rowHeight={getRowHeight}
rowRenderer={rowRenderer}
overscanRowCount={15}
/>
)}
</AutoSizer>
</div>
);
};
Expand Down

0 comments on commit 6ca5aeb

Please sign in to comment.