This is an automated email from the ASF dual-hosted git repository.

kgabryje pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 06fb330569 perf: Optimize DashboardPage and SyncDashboardState (#31244)
06fb330569 is described below

commit 06fb330569ccbaa7264538fd71c3e2a208df031f
Author: Kamil Gabryjelski <[email protected]>
AuthorDate: Mon Dec 2 16:10:02 2024 +0100

    perf: Optimize DashboardPage and SyncDashboardState (#31244)
---
 .../DashboardBuilder/DashboardBuilder.tsx          |  8 +--
 .../components/SyncDashboardState/index.tsx        | 68 ++++++++++++----------
 .../src/dashboard/containers/DashboardPage.tsx     | 46 +++++++++------
 .../src/types/DashboardContextForExplore.ts        |  1 +
 4 files changed, 68 insertions(+), 55 deletions(-)

diff --git 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
index 30c61f0af6..e5caed6968 100644
--- 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
+++ 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
@@ -18,7 +18,7 @@
  */
 /* eslint-env browser */
 import cx from 'classnames';
-import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import { memo, useCallback, useEffect, useMemo, useRef, useState } from 
'react';
 import {
   addAlpha,
   css,
@@ -82,8 +82,6 @@ import DashboardContainer from './DashboardContainer';
 import { useNativeFilters } from './state';
 import DashboardWrapper from './DashboardWrapper';
 
-type DashboardBuilderProps = {};
-
 // @z-index-above-dashboard-charts + 1 = 11
 const FiltersPanel = styled.div<{ width: number; hidden: boolean }>`
   grid-column: 1;
@@ -374,7 +372,7 @@ const ELEMENT_ON_SCREEN_OPTIONS = {
   threshold: [1],
 };
 
-const DashboardBuilder: FC<DashboardBuilderProps> = () => {
+const DashboardBuilder = () => {
   const dispatch = useDispatch();
   const uiConfig = useUiConfig();
   const theme = useTheme();
@@ -737,4 +735,4 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
   );
 };
 
-export default DashboardBuilder;
+export default memo(DashboardBuilder);
diff --git 
a/superset-frontend/src/dashboard/components/SyncDashboardState/index.tsx 
b/superset-frontend/src/dashboard/components/SyncDashboardState/index.tsx
index fab9b9672d..59199ec278 100644
--- a/superset-frontend/src/dashboard/components/SyncDashboardState/index.tsx
+++ b/superset-frontend/src/dashboard/components/SyncDashboardState/index.tsx
@@ -18,8 +18,9 @@
  */
 import { FC, useEffect } from 'react';
 
-import { pick } from 'lodash';
-import { shallowEqual, useSelector } from 'react-redux';
+import { pick, pickBy } from 'lodash';
+import { useSelector } from 'react-redux';
+import { createSelector } from '@reduxjs/toolkit';
 import { DashboardContextForExplore } from 
'src/types/DashboardContextForExplore';
 import {
   getItem,
@@ -42,11 +43,7 @@ export const getDashboardContextLocalStorage = () => {
   // A new dashboard tab id is generated on each dashboard page opening.
   // We mark ids as redundant when user leaves the dashboard, because they 
won't be reused.
   // Then we remove redundant dashboard contexts from local storage in order 
not to clutter it
-  return Object.fromEntries(
-    Object.entries(dashboardsContexts).filter(
-      ([, value]) => !value.isRedundant,
-    ),
-  );
+  return pickBy(dashboardsContexts, value => !value.isRedundant);
 };
 
 const updateDashboardTabLocalStorage = (
@@ -56,38 +53,45 @@ const updateDashboardTabLocalStorage = (
   const dashboardsContexts = getDashboardContextLocalStorage();
   setItem(LocalStorageKeys.DashboardExploreContext, {
     ...dashboardsContexts,
-    [dashboardPageId]: dashboardContext,
+    [dashboardPageId]: { ...dashboardContext, dashboardPageId },
   });
 };
 
-const SyncDashboardState: FC<Props> = ({ dashboardPageId }) => {
-  const dashboardContextForExplore = useSelector<
-    RootState,
-    DashboardContextForExplore
-  >(
-    ({ dashboardInfo, dashboardState, nativeFilters, dataMask }) => ({
-      labelsColor: dashboardInfo.metadata?.label_colors || EMPTY_OBJECT,
-      labelsColorMap: dashboardInfo.metadata?.map_label_colors || EMPTY_OBJECT,
+const selectDashboardContextForExplore = createSelector(
+  [
+    (state: RootState) => state.dashboardInfo.metadata,
+    (state: RootState) => state.dashboardInfo.id,
+    (state: RootState) => state.dashboardState?.colorScheme,
+    (state: RootState) => state.nativeFilters?.filters,
+    (state: RootState) => state.dataMask,
+  ],
+  (metadata, dashboardId, colorScheme, filters, dataMask) => {
+    const nativeFilters = Object.keys(filters).reduce((acc, key) => {
+      acc[key] = pick(filters[key], ['chartsInScope']);
+      return acc;
+    }, {});
+
+    return {
+      labelsColor: metadata?.label_colors || EMPTY_OBJECT,
+      labelsColorMap: metadata?.map_label_colors || EMPTY_OBJECT,
       sharedLabelsColors: enforceSharedLabelsColorsArray(
-        dashboardInfo.metadata?.shared_label_colors,
-      ),
-      colorScheme: dashboardState?.colorScheme,
-      chartConfiguration:
-        dashboardInfo.metadata?.chart_configuration || EMPTY_OBJECT,
-      nativeFilters: Object.entries(nativeFilters.filters).reduce(
-        (acc, [key, filterValue]) => ({
-          ...acc,
-          [key]: pick(filterValue, ['chartsInScope']),
-        }),
-        {},
+        metadata?.shared_label_colors,
       ),
+      colorScheme,
+      chartConfiguration: metadata?.chart_configuration || EMPTY_OBJECT,
+      nativeFilters,
       dataMask,
-      dashboardId: dashboardInfo.id,
+      dashboardId,
       filterBoxFilters: getActiveFilters(),
-      dashboardPageId,
-    }),
-    shallowEqual,
-  );
+    };
+  },
+);
+
+const SyncDashboardState: FC<Props> = ({ dashboardPageId }) => {
+  const dashboardContextForExplore = useSelector<
+    RootState,
+    DashboardContextForExplore
+  >(selectDashboardContextForExplore);
 
   useEffect(() => {
     updateDashboardTabLocalStorage(dashboardPageId, 
dashboardContextForExplore);
diff --git a/superset-frontend/src/dashboard/containers/DashboardPage.tsx 
b/superset-frontend/src/dashboard/containers/DashboardPage.tsx
index d8ddf0f19e..fb8bb6c05d 100644
--- a/superset-frontend/src/dashboard/containers/DashboardPage.tsx
+++ b/superset-frontend/src/dashboard/containers/DashboardPage.tsx
@@ -83,16 +83,20 @@ const selectRelevantDatamask = createSelector(
   dataMask => getRelevantDataMask(dataMask, 'ownState'), // the second 
parameter conducts the transformation
 );
 
+const selectChartConfiguration = (state: RootState) =>
+  state.dashboardInfo.metadata?.chart_configuration;
+const selectNativeFilters = (state: RootState) => state.nativeFilters.filters;
+const selectDataMask = (state: RootState) => state.dataMask;
+const selectAllSliceIds = (state: RootState) => state.dashboardState.sliceIds;
 // TODO: move to Dashboard.jsx when it's refactored to functional component
 const selectActiveFilters = createSelector(
-  (state: RootState) => ({
-    // eslint-disable-next-line camelcase
-    chartConfiguration: state.dashboardInfo.metadata?.chart_configuration,
-    nativeFilters: state.nativeFilters.filters,
-    dataMask: state.dataMask,
-    allSliceIds: state.dashboardState.sliceIds,
-  }),
-  ({ chartConfiguration, nativeFilters, dataMask, allSliceIds }) => ({
+  [
+    selectChartConfiguration,
+    selectNativeFilters,
+    selectDataMask,
+    selectAllSliceIds,
+  ],
+  (chartConfiguration, nativeFilters, dataMask, allSliceIds) => ({
     ...getActiveFilters(),
     ...getAllActiveFilters({
       // eslint-disable-next-line camelcase
@@ -228,17 +232,23 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug 
}: PageProps) => {
 
   if (error) throw error; // caught in error boundary
 
+  const globalStyles = useMemo(
+    () => [
+      filterCardPopoverStyle(theme),
+      headerStyles(theme),
+      chartContextMenuStyles(theme),
+      focusStyle(theme),
+      chartHeaderStyles(theme),
+    ],
+    [theme],
+  );
+
+  if (error) throw error; // caught in error boundary
+
+  const DashboardBuilderComponent = useMemo(() => <DashboardBuilder />, []);
   return (
     <>
-      <Global
-        styles={[
-          filterCardPopoverStyle(theme),
-          headerStyles(theme),
-          chartContextMenuStyles(theme),
-          focusStyle(theme),
-          chartHeaderStyles(theme),
-        ]}
-      />
+      <Global styles={globalStyles} />
       {readyToRender && hasDashboardInfoInitiated ? (
         <>
           <SyncDashboardState dashboardPageId={dashboardPageId} />
@@ -247,7 +257,7 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: 
PageProps) => {
               activeFilters={activeFilters}
               ownDataCharts={relevantDataMask}
             >
-              <DashboardBuilder />
+              {DashboardBuilderComponent}
             </DashboardContainer>
           </DashboardPageIdContext.Provider>
         </>
diff --git a/superset-frontend/src/types/DashboardContextForExplore.ts 
b/superset-frontend/src/types/DashboardContextForExplore.ts
index 117a182606..e9e964a1a3 100644
--- a/superset-frontend/src/types/DashboardContextForExplore.ts
+++ b/superset-frontend/src/types/DashboardContextForExplore.ts
@@ -41,4 +41,5 @@ export interface DashboardContextForExplore {
       }
     | {};
   isRedundant?: boolean;
+  dashboardPageId?: string;
 }

Reply via email to