perf(dashboard): reduce rerenders of DragDroppable (#16525)
* perf(dashboard): reduce rerenders of DragDroppable
* lint fix
diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
index cc897b8..d16e761 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 React, { FC } from 'react';
+import React, { FC, useCallback, useMemo } from 'react';
import { JsonObject, styled, css } from '@superset-ui/core';
import ErrorBoundary from 'src/components/ErrorBoundary';
import BuilderComponentPane from 'src/dashboard/components/BuilderComponentPane';
@@ -157,15 +157,14 @@
state => state.dashboardState.fullSizeChartId,
);
- const handleChangeTab = ({
- pathToTabIndex,
- }: {
- pathToTabIndex: string[];
- }) => {
- dispatch(setDirectPathToChild(pathToTabIndex));
- };
+ const handleChangeTab = useCallback(
+ ({ pathToTabIndex }: { pathToTabIndex: string[] }) => {
+ dispatch(setDirectPathToChild(pathToTabIndex));
+ },
+ [dispatch],
+ );
- const handleDeleteTopLevelTabs = () => {
+ const handleDeleteTopLevelTabs = useCallback(() => {
dispatch(deleteTopLevelTabs());
const firstTab = getDirectPathToTabIndex(
@@ -173,7 +172,12 @@
0,
);
dispatch(setDirectPathToChild(firstTab));
- };
+ }, [dashboardLayout, dispatch]);
+
+ const handleDrop = useCallback(
+ dropResult => dispatch(handleComponentDrop(dropResult)),
+ [dispatch],
+ );
const dashboardRoot = dashboardLayout[DASHBOARD_ROOT_ID];
const rootChildId = dashboardRoot.children[0];
@@ -217,6 +221,54 @@
const filterBarHeight = `calc(100vh - ${offset}px)`;
const filterBarOffset = dashboardFiltersOpen ? 0 : barTopOffset + 20;
+ const draggableStyle = useMemo(
+ () => ({
+ marginLeft: dashboardFiltersOpen || editMode ? 0 : -32,
+ }),
+ [dashboardFiltersOpen, editMode],
+ );
+
+ const renderDraggableContent = useCallback(
+ ({ dropIndicatorProps }: { dropIndicatorProps: JsonObject }) => (
+ <div>
+ {!hideDashboardHeader && <DashboardHeader />}
+ {dropIndicatorProps && <div {...dropIndicatorProps} />}
+ {!isReport && topLevelTabs && (
+ <WithPopoverMenu
+ shouldFocus={shouldFocusTabs}
+ menuItems={[
+ <IconButton
+ icon={<Icons.FallOutlined iconSize="xl" />}
+ label="Collapse tab content"
+ onClick={handleDeleteTopLevelTabs}
+ />,
+ ]}
+ editMode={editMode}
+ >
+ {/* @ts-ignore */}
+ <DashboardComponent
+ id={topLevelTabs?.id}
+ parentId={DASHBOARD_ROOT_ID}
+ depth={DASHBOARD_ROOT_DEPTH + 1}
+ index={0}
+ renderTabContent={false}
+ renderHoverMenu={false}
+ onChangeTab={handleChangeTab}
+ />
+ </WithPopoverMenu>
+ )}
+ </div>
+ ),
+ [
+ editMode,
+ handleChangeTab,
+ handleDeleteTopLevelTabs,
+ hideDashboardHeader,
+ isReport,
+ topLevelTabs,
+ ],
+ );
+
return (
<StyledDiv>
{nativeFiltersEnabled && !editMode && (
@@ -244,45 +296,13 @@
depth={DASHBOARD_ROOT_DEPTH}
index={0}
orientation="column"
- onDrop={dropResult => dispatch(handleComponentDrop(dropResult))}
+ onDrop={handleDrop}
editMode={editMode}
// you cannot drop on/displace tabs if they already exist
disableDragDrop={!!topLevelTabs}
- style={{
- marginLeft: dashboardFiltersOpen || editMode ? 0 : -32,
- }}
+ style={draggableStyle}
>
- {({ dropIndicatorProps }: { dropIndicatorProps: JsonObject }) => (
- <div>
- {!hideDashboardHeader && <DashboardHeader />}
- {dropIndicatorProps && <div {...dropIndicatorProps} />}
- {!isReport && topLevelTabs && (
- <WithPopoverMenu
- shouldFocus={shouldFocusTabs}
- menuItems={[
- <IconButton
- icon={<Icons.FallOutlined iconSize="xl" />}
- label="Collapse tab content"
- onClick={handleDeleteTopLevelTabs}
- />,
- ]}
- editMode={editMode}
- >
- {/*
- // @ts-ignore */}
- <DashboardComponent
- id={topLevelTabs?.id}
- parentId={DASHBOARD_ROOT_ID}
- depth={DASHBOARD_ROOT_DEPTH + 1}
- index={0}
- renderTabContent={false}
- renderHoverMenu={false}
- onChangeTab={handleChangeTab}
- />
- </WithPopoverMenu>
- )}
- </div>
- )}
+ {renderDraggableContent}
</DragDroppable>
</StyledHeader>
<StyledContent fullSizeChartId={fullSizeChartId}>
diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.jsx b/superset-frontend/src/dashboard/components/DashboardGrid.jsx
index 9fb0fb5..ad03859 100644
--- a/superset-frontend/src/dashboard/components/DashboardGrid.jsx
+++ b/superset-frontend/src/dashboard/components/DashboardGrid.jsx
@@ -38,6 +38,16 @@
const defaultProps = {};
+const renderDraggableContentBottom = dropProps =>
+ dropProps.dropIndicatorProps && (
+ <div className="drop-indicator drop-indicator--bottom" />
+ );
+
+const renderDraggableContentTop = dropProps =>
+ dropProps.dropIndicatorProps && (
+ <div className="drop-indicator drop-indicator--top" />
+ );
+
class DashboardGrid extends React.PureComponent {
constructor(props) {
super(props);
@@ -144,11 +154,7 @@
className="empty-droptarget"
editMode
>
- {({ dropIndicatorProps }) =>
- dropIndicatorProps && (
- <div className="drop-indicator drop-indicator--bottom" />
- )
- }
+ {renderDraggableContentBottom}
</DragDroppable>
)}
@@ -181,11 +187,7 @@
className="empty-droptarget"
editMode
>
- {({ dropIndicatorProps }) =>
- dropIndicatorProps && (
- <div className="drop-indicator drop-indicator--top" />
- )
- }
+ {renderDraggableContentTop}
</DragDroppable>
)}
diff --git a/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx b/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
index a841be7..a29d047 100644
--- a/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
+++ b/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
@@ -66,7 +66,7 @@
};
// export unwrapped component for testing
-export class UnwrappedDragDroppable extends React.Component {
+export class UnwrappedDragDroppable extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
index faa1e04..17691b8 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
@@ -74,6 +74,16 @@
`}
`;
+const renderDraggableContentBottom = dropProps =>
+ dropProps.dropIndicatorProps && (
+ <div className="drop-indicator drop-indicator--bottom" />
+ );
+
+const renderDraggableContentTop = dropProps =>
+ dropProps.dropIndicatorProps && (
+ <div className="drop-indicator drop-indicator--top" />
+ );
+
export default class Tab extends React.PureComponent {
constructor(props) {
super(props);
@@ -148,11 +158,7 @@
editMode
className="empty-droptarget"
>
- {({ dropIndicatorProps }) =>
- dropIndicatorProps && (
- <div className="drop-indicator drop-indicator--top" />
- )
- }
+ {renderDraggableContentTop}
</DragDroppable>
)}
{tabComponent.children.map((componentId, componentIndex) => (
@@ -184,11 +190,7 @@
editMode
className="empty-droptarget"
>
- {({ dropIndicatorProps }) =>
- dropIndicatorProps && (
- <div className="drop-indicator drop-indicator--bottom" />
- )
- }
+ {renderDraggableContentBottom}
</DragDroppable>
)}
</div>
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx
index 9916520..1966dd5 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx
@@ -129,6 +129,7 @@
this.handleDeleteComponent = this.handleDeleteComponent.bind(this);
this.handleDeleteTab = this.handleDeleteTab.bind(this);
this.handleDropOnTab = this.handleDropOnTab.bind(this);
+ this.handleDrop = this.handleDrop.bind(this);
}
componentDidMount() {
@@ -281,6 +282,12 @@
}
}
+ handleDrop(dropResult) {
+ if (dropResult.dragging.type !== TABS_TYPE) {
+ this.props.handleComponentDrop(dropResult);
+ }
+ }
+
render() {
const {
depth,
@@ -292,7 +299,6 @@
onResizeStart,
onResize,
onResizeStop,
- handleComponentDrop,
renderTabContent,
renderHoverMenu,
isComponentVisible: isCurrentTabVisible,
@@ -315,11 +321,7 @@
orientation="row"
index={index}
depth={depth}
- onDrop={dropResult => {
- if (dropResult.dragging.type !== TABS_TYPE) {
- handleComponentDrop(dropResult);
- }
- }}
+ onDrop={this.handleDrop}
editMode={editMode}
>
{({