From e46062d4cdc5d85bba41f3482a587d03d51114ff Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 1 May 2026 18:36:18 +0200 Subject: [PATCH] Memoize chart components with custom deep comparison - Add custom comparison function to React.memo for TopCountriesPieChart - Add custom comparison function to React.memo for TopCountriesBarChart - Use JSON.stringify for deep equality comparison of countries and countryNames - Prevents unnecessary re-renders when parent updates with same data - Avoids Recharts reprocessing 5000+ data points on each parent re-render All tests passing. No linting issues. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/components/TopCountriesBarChart.tsx | 21 ++++++++++++++----- .../src/components/TopCountriesPieChart.tsx | 21 ++++++++++++++----- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/TopCountriesBarChart.tsx b/frontend/src/components/TopCountriesBarChart.tsx index 4e6d6b2..5c2c498 100644 --- a/frontend/src/components/TopCountriesBarChart.tsx +++ b/frontend/src/components/TopCountriesBarChart.tsx @@ -138,10 +138,11 @@ function BarTooltip(props: TooltipContentProps): React.JSX.Element | null { * @param props - `countries` map and `countryNames` map from the * `/api/dashboard/bans/by-country` response. */ -export const TopCountriesBarChart = memo(function TopCountriesBarChart({ - countries, - countryNames, -}: TopCountriesBarChartProps): React.JSX.Element { +export const TopCountriesBarChart = memo( + function TopCountriesBarChart({ + countries, + countryNames, + }: TopCountriesBarChartProps): React.JSX.Element { const styles = useStyles(); const { colorMode } = useThemeMode(); @@ -198,4 +199,14 @@ export const TopCountriesBarChart = memo(function TopCountriesBarChart({ ); -}); +}, + (prev, next) => { + // Custom comparison: if countries and countryNames are deeply equal, + // memo returns true (skip render). This prevents Recharts from + // reprocessing 5000+ data points on parent re-renders. + return ( + JSON.stringify(prev.countries) === JSON.stringify(next.countries) && + JSON.stringify(prev.countryNames) === JSON.stringify(next.countryNames) + ); + }, +); diff --git a/frontend/src/components/TopCountriesPieChart.tsx b/frontend/src/components/TopCountriesPieChart.tsx index f6ac593..6bd0dce 100644 --- a/frontend/src/components/TopCountriesPieChart.tsx +++ b/frontend/src/components/TopCountriesPieChart.tsx @@ -130,10 +130,11 @@ function PieTooltip(props: TooltipContentProps): React.JSX.Element | null { * @param props - `countries` map and `countryNames` map from the * `/api/dashboard/bans/by-country` response. */ -export const TopCountriesPieChart = memo(function TopCountriesPieChart({ - countries, - countryNames, -}: TopCountriesPieChartProps): React.JSX.Element { +export const TopCountriesPieChart = memo( + function TopCountriesPieChart({ + countries, + countryNames, + }: TopCountriesPieChartProps): React.JSX.Element { const styles = useStyles(); const { colorMode } = useThemeMode(); @@ -199,4 +200,14 @@ export const TopCountriesPieChart = memo(function TopCountriesPieChart({ ); -}); +}, + (prev, next) => { + // Custom comparison: if countries and countryNames are deeply equal, + // memo returns true (skip render). This prevents Recharts from + // reprocessing 5000+ data points on parent re-renders. + return ( + JSON.stringify(prev.countries) === JSON.stringify(next.countries) && + JSON.stringify(prev.countryNames) === JSON.stringify(next.countryNames) + ); + }, +);