From 55c00bdd9044989ab5dc20d385e5e3c70330fc3a Mon Sep 17 00:00:00 2001
From: Uladzimir Havenchyk <125459446+uladzimirdev@users.noreply.github.com>
Date: Fri, 31 May 2024 18:33:28 +0300
Subject: [PATCH] Avoid unnecessary re-renderings in dashcards (#43323)

* Avoid unnecessary re-renderings in dashcards

* replace useCallback with useMemo
---
 .../components/DashCard/DashCard.tsx          | 12 ++----
 .../DashCardActionsPanel.tsx                  | 39 ++++++++++++++++---
 .../DashCard/DashCardVisualization.tsx        | 22 ++++++++---
 3 files changed, 54 insertions(+), 19 deletions(-)

diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCard.tsx b/frontend/src/metabase/dashboard/components/DashCard/DashCard.tsx
index ceded230b4d..bd86e7ce76c 100644
--- a/frontend/src/metabase/dashboard/components/DashCard/DashCard.tsx
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCard.tsx
@@ -317,11 +317,9 @@ function DashCardInner({
             onAddSeries={() => onAddSeries(dashcard)}
             onRemove={() => onRemove(dashcard)}
             onReplaceCard={() => onReplaceCard(dashcard)}
-            onUpdateVisualizationSettings={settings =>
-              onUpdateVisualizationSettings(dashcard.id, settings)
-            }
-            onReplaceAllVisualizationSettings={settings =>
-              onReplaceAllVisualizationSettings(dashcard.id, settings)
+            onUpdateVisualizationSettings={onUpdateVisualizationSettings}
+            onReplaceAllVisualizationSettings={
+              onReplaceAllVisualizationSettings
             }
             showClickBehaviorSidebar={handleShowClickBehaviorSidebar}
             onPreviewToggle={handlePreviewToggle}
@@ -354,9 +352,7 @@ function DashCardInner({
           isMobile={isMobile}
           isPublicOrEmbedded={isPublicOrEmbedded}
           showClickBehaviorSidebar={showClickBehaviorSidebar}
-          onUpdateVisualizationSettings={settings =>
-            onUpdateVisualizationSettings(dashcard.id, settings)
-          }
+          onUpdateVisualizationSettings={onUpdateVisualizationSettings}
           onChangeCardAndRun={
             navigateToNewCardFromDashboard ? changeCardAndRunHandler : null
           }
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardActionsPanel/DashCardActionsPanel.tsx b/frontend/src/metabase/dashboard/components/DashCard/DashCardActionsPanel/DashCardActionsPanel.tsx
index 554e177c285..0f19a875c77 100644
--- a/frontend/src/metabase/dashboard/components/DashCard/DashCardActionsPanel/DashCardActionsPanel.tsx
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardActionsPanel/DashCardActionsPanel.tsx
@@ -1,5 +1,5 @@
 import type { MouseEvent } from "react";
-import { useState } from "react";
+import { useCallback, useState } from "react";
 import { t } from "ttag";
 
 import { isActionDashCard } from "metabase/actions/utils";
@@ -9,6 +9,7 @@ import { getVisualizationRaw } from "metabase/visualizations";
 import type {
   Dashboard,
   DashboardCard,
+  DashCardId,
   Series,
   VisualizationSettings,
 } from "metabase-types/api";
@@ -35,8 +36,12 @@ interface Props {
   onRemove: () => void;
   onAddSeries: () => void;
   onReplaceCard: () => void;
-  onReplaceAllVisualizationSettings: (settings: VisualizationSettings) => void;
+  onReplaceAllVisualizationSettings: (
+    dashcardId: DashCardId,
+    settings: VisualizationSettings,
+  ) => void;
   onUpdateVisualizationSettings: (
+    dashcardId: DashCardId,
     settings: Partial<VisualizationSettings>,
   ) => void;
   showClickBehaviorSidebar: () => void;
@@ -73,6 +78,28 @@ export function DashCardActionsPanel({
 
   const [isDashCardTabMenuOpen, setIsDashCardTabMenuOpen] = useState(false);
 
+  const handleOnUpdateVisualizationSettings = useCallback(
+    (settings: VisualizationSettings) => {
+      if (!dashcard) {
+        return;
+      }
+
+      onUpdateVisualizationSettings(dashcard.id, settings);
+    },
+    [dashcard, onUpdateVisualizationSettings],
+  );
+
+  const handleOnReplaceAllVisualizationSettings = useCallback(
+    (settings: VisualizationSettings) => {
+      if (!dashcard) {
+        return;
+      }
+
+      onReplaceAllVisualizationSettings(dashcard.id, settings);
+    },
+    [dashcard, onReplaceAllVisualizationSettings],
+  );
+
   if (dashcard) {
     buttons.push(
       <DashCardTabMenu
@@ -103,14 +130,16 @@ export function DashCardActionsPanel({
   }
 
   if (!isLoading && !hasError) {
-    if (onReplaceAllVisualizationSettings && !disableSettingsConfig) {
+    if (!disableSettingsConfig) {
       buttons.push(
         <ChartSettingsButton
           key="chart-settings-button"
           series={series}
           dashboard={dashboard}
           dashcard={dashcard}
-          onReplaceAllVisualizationSettings={onReplaceAllVisualizationSettings}
+          onReplaceAllVisualizationSettings={
+            handleOnReplaceAllVisualizationSettings
+          }
         />,
       );
     }
@@ -183,7 +212,7 @@ export function DashCardActionsPanel({
         <LinkCardEditButton
           key="link-edit-button"
           dashcard={dashcard}
-          onUpdateVisualizationSettings={onUpdateVisualizationSettings}
+          onUpdateVisualizationSettings={handleOnUpdateVisualizationSettings}
         />,
       );
     }
diff --git a/frontend/src/metabase/dashboard/components/DashCard/DashCardVisualization.tsx b/frontend/src/metabase/dashboard/components/DashCard/DashCardVisualization.tsx
index e1c4c32a0de..c4b70ec79bf 100644
--- a/frontend/src/metabase/dashboard/components/DashCard/DashCardVisualization.tsx
+++ b/frontend/src/metabase/dashboard/components/DashCard/DashCardVisualization.tsx
@@ -76,7 +76,10 @@ interface DashCardVisualizationProps {
   error?: { message?: string; icon?: IconName };
   headerIcon?: IconProps;
 
-  onUpdateVisualizationSettings: (settings: VisualizationSettings) => void;
+  onUpdateVisualizationSettings: (
+    id: DashCardId,
+    settings: VisualizationSettings,
+  ) => void;
   onChangeCardAndRun: DashCardOnChangeCardAndRunHandler | null;
   showClickBehaviorSidebar: (dashCardId: DashCardId | null) => void;
   onChangeLocation: (location: LocationDescriptor) => void;
@@ -123,7 +126,14 @@ export function DashCardVisualization({
       : null;
   }, [dashcard.card, metadata]);
 
-  const renderVisualizationOverlay = useCallback(() => {
+  const handleOnUpdateVisualizationSettings = useCallback(
+    (settings: VisualizationSettings) => {
+      onUpdateVisualizationSettings(dashcard.id, settings);
+    },
+    [dashcard.id, onUpdateVisualizationSettings],
+  );
+
+  const visualizationOverlay = useMemo(() => {
     if (isClickBehaviorSidebarOpen) {
       const disableClickBehavior =
         getVisualizationRaw(series)?.disableClickBehavior;
@@ -177,7 +187,7 @@ export function DashCardVisualization({
     series,
   ]);
 
-  const renderActionButtons = useCallback(() => {
+  const actionButtons = useMemo(() => {
     if (!question) {
       return null;
     }
@@ -251,10 +261,10 @@ export function DashCardVisualization({
       isPreviewing={isPreviewing}
       isEditingParameter={isEditingParameter}
       isMobile={isMobile}
-      actionButtons={renderActionButtons()}
-      replacementContent={renderVisualizationOverlay()}
+      actionButtons={actionButtons}
+      replacementContent={visualizationOverlay}
       getExtraDataForClick={getExtraDataForClick}
-      onUpdateVisualizationSettings={onUpdateVisualizationSettings}
+      onUpdateVisualizationSettings={handleOnUpdateVisualizationSettings}
       onChangeCardAndRun={onChangeCardAndRun}
       onChangeLocation={onChangeLocation}
     />
-- 
GitLab