mirror of
https://github.com/Radarr/Radarr
synced 2026-01-25 08:53:02 +01:00
fix(frontend): memoize inline JSX objects for performance (#87)
- MovieIndexTable: memoize itemData, move row flex styles to CSS - MovieIndexOverviews: memoize itemData, extract listStyle constant - MovieIndexOverview: memoize elementStyle and infoStyle - CircularProgressBar: memoize containerStyle and circleStyle Reduces unnecessary re-renders in virtualized lists and frequently rendered components. Closes #41 Co-authored-by: admin <admin@ardentleatherworks.com>
This commit is contained in:
parent
80c364110c
commit
1230212df8
5 changed files with 78 additions and 52 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import styles from './CircularProgressBar.css';
|
||||
|
||||
interface CircularProgressBarProps {
|
||||
|
|
@ -59,15 +59,24 @@ function CircularProgressBar({
|
|||
[]
|
||||
);
|
||||
|
||||
const containerStyle = useMemo(() => {
|
||||
return {
|
||||
width: sizeInPixels,
|
||||
height: sizeInPixels,
|
||||
lineHeight: sizeInPixels,
|
||||
};
|
||||
}, [sizeInPixels]);
|
||||
|
||||
const circleStyle = useMemo(() => {
|
||||
return {
|
||||
stroke: strokeColor,
|
||||
strokeWidth,
|
||||
strokeDashoffset,
|
||||
};
|
||||
}, [strokeColor, strokeWidth, strokeDashoffset]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={containerClassName}
|
||||
style={{
|
||||
width: sizeInPixels,
|
||||
height: sizeInPixels,
|
||||
lineHeight: sizeInPixels,
|
||||
}}
|
||||
>
|
||||
<div className={containerClassName} style={containerStyle}>
|
||||
<svg
|
||||
className={className}
|
||||
version="1.1"
|
||||
|
|
@ -81,11 +90,7 @@ function CircularProgressBar({
|
|||
cx={center}
|
||||
cy={center}
|
||||
strokeDasharray={circumference}
|
||||
style={{
|
||||
stroke: strokeColor,
|
||||
strokeWidth,
|
||||
strokeDashoffset,
|
||||
}}
|
||||
style={circleStyle}
|
||||
/>
|
||||
</svg>
|
||||
|
||||
|
|
|
|||
|
|
@ -124,10 +124,12 @@ function MovieIndexOverview(props: Readonly<MovieIndexOverviewProps>) {
|
|||
|
||||
const link = `/movie/${tmdbId}`;
|
||||
|
||||
const elementStyle = {
|
||||
width: `${posterWidth}px`,
|
||||
height: `${posterHeight}px`,
|
||||
};
|
||||
const elementStyle = useMemo(() => {
|
||||
return {
|
||||
width: `${posterWidth}px`,
|
||||
height: `${posterHeight}px`,
|
||||
};
|
||||
}, [posterWidth, posterHeight]);
|
||||
|
||||
const contentHeight = useMemo(() => {
|
||||
const padding = isSmallScreen ? columnPaddingSmallScreen : columnPadding;
|
||||
|
|
@ -135,6 +137,10 @@ function MovieIndexOverview(props: Readonly<MovieIndexOverviewProps>) {
|
|||
return rowHeight - padding;
|
||||
}, [rowHeight, isSmallScreen]);
|
||||
|
||||
const infoStyle = useMemo(() => {
|
||||
return { maxHeight: contentHeight };
|
||||
}, [contentHeight]);
|
||||
|
||||
const overviewHeight = contentHeight - titleRowHeight;
|
||||
|
||||
return (
|
||||
|
|
@ -175,7 +181,7 @@ function MovieIndexOverview(props: Readonly<MovieIndexOverviewProps>) {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.info} style={{ maxHeight: contentHeight }}>
|
||||
<div className={styles.info} style={infoStyle}>
|
||||
<div className={styles.titleRow}>
|
||||
<Link className={styles.title} to={link}>
|
||||
{title}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ const bodyPaddingSmallScreen = Number.parseInt(
|
|||
dimensions.pageContentBodyPaddingSmallScreen
|
||||
);
|
||||
|
||||
const listStyle = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'none',
|
||||
} as const;
|
||||
|
||||
interface RowItemData {
|
||||
items: Movie[];
|
||||
sortKey: string;
|
||||
|
|
@ -179,28 +185,36 @@ function MovieIndexOverviews(props: Readonly<MovieIndexOverviewsProps>) {
|
|||
}
|
||||
}, [jumpToCharacter, rowHeight, items, scrollerRef, listRef]);
|
||||
|
||||
const itemData = useMemo(() => {
|
||||
return {
|
||||
items,
|
||||
sortKey,
|
||||
posterWidth,
|
||||
posterHeight,
|
||||
rowHeight,
|
||||
isSelectMode,
|
||||
isSmallScreen,
|
||||
};
|
||||
}, [
|
||||
items,
|
||||
sortKey,
|
||||
posterWidth,
|
||||
posterHeight,
|
||||
rowHeight,
|
||||
isSelectMode,
|
||||
isSmallScreen,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div ref={measureRef}>
|
||||
<List<RowItemData>
|
||||
ref={listRef}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'none',
|
||||
}}
|
||||
style={listStyle}
|
||||
width={size.width}
|
||||
height={size.height}
|
||||
itemCount={items.length}
|
||||
itemSize={rowHeight}
|
||||
itemData={{
|
||||
items,
|
||||
sortKey,
|
||||
posterWidth,
|
||||
posterHeight,
|
||||
rowHeight,
|
||||
isSelectMode,
|
||||
isSmallScreen,
|
||||
}}
|
||||
itemData={itemData}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: background-color 500ms;
|
||||
|
||||
&:hover {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ const bodyPaddingSmallScreen = Number.parseInt(
|
|||
dimensions.pageContentBodyPaddingSmallScreen
|
||||
);
|
||||
|
||||
const listStyle = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'none',
|
||||
} as const;
|
||||
|
||||
interface RowItemData {
|
||||
items: Movie[];
|
||||
sortKey: string;
|
||||
|
|
@ -53,14 +59,7 @@ function Row({ index, style, data }: ListChildComponentProps<RowItemData>) {
|
|||
const movie = items[index];
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
...style,
|
||||
}}
|
||||
className={styles.row}
|
||||
>
|
||||
<div style={style} className={styles.row}>
|
||||
<MovieIndexRow
|
||||
movieId={movie.id}
|
||||
sortKey={sortKey}
|
||||
|
|
@ -167,6 +166,15 @@ function MovieIndexTable(props: Readonly<MovieIndexTableProps>) {
|
|||
}
|
||||
}, [jumpToCharacter, rowHeight, items, scrollerRef, listRef]);
|
||||
|
||||
const itemData = useMemo(() => {
|
||||
return {
|
||||
items,
|
||||
sortKey,
|
||||
columns,
|
||||
isSelectMode,
|
||||
};
|
||||
}, [items, sortKey, columns, isSelectMode]);
|
||||
|
||||
return (
|
||||
<div ref={measureRef}>
|
||||
<Scroller className={styles.tableScroller} scrollDirection="horizontal">
|
||||
|
|
@ -178,21 +186,12 @@ function MovieIndexTable(props: Readonly<MovieIndexTableProps>) {
|
|||
/>
|
||||
<List<RowItemData>
|
||||
ref={listRef}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'none',
|
||||
}}
|
||||
style={listStyle}
|
||||
width={size.width}
|
||||
height={size.height}
|
||||
itemCount={items.length}
|
||||
itemSize={rowHeight}
|
||||
itemData={{
|
||||
items,
|
||||
sortKey,
|
||||
columns,
|
||||
isSelectMode,
|
||||
}}
|
||||
itemData={itemData}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
|
|
|
|||
Loading…
Reference in a new issue