Performance improvements (#352)

* Optimize Sidebar performance

* Optimize Dialog
This commit is contained in:
Piotr Rogowski 2021-12-26 20:57:00 +01:00 committed by GitHub
parent 25228fba25
commit caf6a359b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 113 additions and 90 deletions

View File

@ -111,19 +111,8 @@ const Dialog = ({
const isDataReady = Object.keys(tune.constants).length && Object.keys(config.constants).length;
const { storageSet } = useStorage();
const { findConstantOnPage } = useConfig(config);
const [canvasWidth, setCanvasWidth] = useState(0);
const [panelsComponents, setPanelsComponents] = useState<any[]>([]);
const containerRef = useRef<HTMLDivElement | null>(null);
const calculateCanvasWidth = useCallback(() => {
setCanvasWidth((containerRef.current?.clientWidth || 0) - 20);
}, []);
useEffect(() => {
storageSet('lastDialog', url);
calculateCanvasWidth();
window.addEventListener('resize', calculateCanvasWidth);
return () => window.removeEventListener('resize', calculateCanvasWidth);
}, [calculateCanvasWidth, storageSet, url, ui.sidebarCollapsed]);
const renderHelp = (link?: string) => (link &&
<Popover
@ -142,7 +131,7 @@ const Dialog = ({
</Popover>
);
const renderCurve = (curve: CurveType) => {
const renderCurve = useCallback((curve: CurveType) => {
const x = tune.constants[curve.xBins[0]];
const y = tune.constants[curve.yBins[0]];
const xConstant = findConstantOnPage(curve.xBins[0]) as ScalarConstantType;
@ -150,7 +139,6 @@ const Dialog = ({
return (
<Curve
width={canvasWidth}
key={curve.yBins[0]}
disabled={false} // TODO: evaluate condition
help={config.help[curve.yBins[0]]}
@ -162,9 +150,9 @@ const Dialog = ({
yData={parseXy(y.value as string)}
/>
);
};
}, [config.help, findConstantOnPage, tune.constants]);
const renderTable = (table: TableType | RenderedPanel) => {
const renderTable = useCallback((table: TableType | RenderedPanel) => {
const x = tune.constants[table.xBins[0]];
const y = tune.constants[table.yBins[0]];
const z = tune.constants[table.zBins[0]];
@ -181,45 +169,7 @@ const Dialog = ({
yUnits={y.units as string}
/>
</div>;
};
if (!isDataReady) {
return skeleton;
}
const dialogConfig = config.dialogs[name];
const curveConfig = config.curves[name];
const tableConfig = config.tables[name];
// standalone dialog / page
if (!dialogConfig) {
if (curveConfig) {
return (
<div ref={containerRef} style={containerStyle}>
<Divider>{curveConfig.title}</Divider>
{renderCurve(curveConfig)}
</div>
);
}
if (tableConfig) {
return (
<div ref={containerRef} style={containerStyle}>
{renderHelp(tableConfig.help)}
<Divider>{tableConfig.title}</Divider>
{renderTable(tableConfig)}
</div>
);
}
return (
<Result
status="warning"
title="Dialog not found"
style={{ marginTop: 50 }}
/>
);
}
}, [tune.constants]);
const calculateSpan = (type: PanelTypes, dialogsCount: number) => {
let xxl = 24;
@ -283,8 +233,9 @@ const Dialog = ({
});
};
// TODO: refactor this
resolveDialogs(config.dialogs, name);
if (config.dialogs) {
resolveDialogs(config.dialogs, name);
}
// remove dummy dialogs and flatten to array
const panels = Object.keys(resolvedDialogs).map((dialogName: string): RenderedPanel => {
@ -324,7 +275,7 @@ const Dialog = ({
};
});
const panelsComponents = () => panels.map((panel: RenderedPanel) => {
const generatePanelsComponents = useCallback(() => panels.map((panel: RenderedPanel) => {
if (panel.type === PanelTypes.FIELDS && panel.fields.length === 0) {
return null;
}
@ -401,7 +352,54 @@ const Dialog = ({
{panel.type === PanelTypes.TABLE && renderTable(panel)}
</Col>
);
});
}), [config, findConstantOnPage, panels, renderCurve, renderTable, tune.constants]);
useEffect(() => {
storageSet('lastDialog', url);
if (isDataReady) {
setPanelsComponents(generatePanelsComponents());
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDataReady, url, ui.sidebarCollapsed]);
if (!isDataReady) {
return skeleton;
}
const dialogConfig = config.dialogs[name];
const curveConfig = config.curves[name];
const tableConfig = config.tables[name];
// standalone dialog / page
if (!dialogConfig) {
if (curveConfig) {
return (
<div ref={containerRef} style={containerStyle}>
<Divider>{curveConfig.title}</Divider>
{renderCurve(curveConfig)}
</div>
);
}
if (tableConfig) {
return (
<div ref={containerRef} style={containerStyle}>
{renderHelp(tableConfig.help)}
<Divider>{tableConfig.title}</Divider>
{renderTable(tableConfig)}
</div>
);
}
return (
<Result
status="warning"
title="Dialog not found"
style={{ marginTop: 50 }}
/>
);
}
return (
<div ref={containerRef} style={containerStyle}>
@ -411,7 +409,7 @@ const Dialog = ({
wrapperCol={{ span: 10 }}
>
<Row gutter={20}>
{isDataReady && panelsComponents()}
{panelsComponents}
</Row>
<Form.Item>
{burnButton}

View File

@ -4,6 +4,7 @@ import {
} from 'antd';
import {
useEffect,
useRef,
useState,
} from 'react';
import UplotReact from 'uplot-react';
@ -15,7 +16,6 @@ import Table from './Curve/Table';
const { useBreakpoint } = Grid;
const Curve = ({
width,
xLabel,
yLabel,
xData,
@ -25,7 +25,6 @@ const Curve = ({
xUnits = '',
yUnits = '',
}: {
width: number,
xLabel: string,
yLabel: string,
xData: number[],
@ -35,6 +34,7 @@ const Curve = ({
xUnits?: string,
yUnits?: string,
}) => {
const containerRef = useRef<HTMLDivElement | null>(null);
const { sm } = useBreakpoint();
const [options, setOptions] = useState<uPlot.Options>();
const [plotData, setPlotData] = useState<uPlot.AlignedData>();
@ -42,7 +42,7 @@ const Curve = ({
useEffect(() => {
setPlotData([xData, yData]);
setOptions({
width,
width: containerRef.current?.clientWidth || 0,
height: 350,
scales: {
x: { time: false },
@ -76,7 +76,7 @@ const Curve = ({
points: { size: 9 },
},
});
}, [width, xData, xLabel, xUnits, yData, yLabel, yUnits, sm]);
}, [xData, xLabel, xUnits, yData, yLabel, yUnits, sm]);
if (!sm) {
return <LandscapeNotice />;
@ -88,15 +88,17 @@ const Curve = ({
<Typography.Text type="secondary">{help}</Typography.Text>
</Typography.Paragraph>
<UplotReact options={options!} data={plotData!} />
<Table
xLabel={xLabel}
yLabel={yLabel}
xData={xData}
yData={yData}
disabled={disabled}
xUnits={xUnits}
yUnits={yUnits}
/>
<div ref={containerRef}>
<Table
xLabel={xLabel}
yLabel={yLabel}
xData={xData}
yData={yData}
disabled={disabled}
xUnits={xUnits}
yUnits={yUnits}
/>
</div>
</>
);
};

View File

@ -35,6 +35,7 @@ import {
OutputChannel,
Logs,
DatalogEntry,
Tune as TuneType,
} from '@speedy-tuner/types';
import { loadLogs } from '../utils/api';
import LogCanvas from './Log/LogCanvas';
@ -44,24 +45,38 @@ import {
msToTime,
} from '../utils/number';
import useConfig from '../hooks/useConfig';
import {
isExpression,
stripExpression,
} from '../utils/tune/expression';
const { TabPane } = Tabs;
const { Content } = Layout;
const { Step } = Steps;
const edgeUnknown = 'Unknown';
const margin = 30;
const sidebarWidth = 250;
const minCanvasHeightInner = 600;
const mapStateToProps = (state: AppState) => ({
ui: state.ui,
tune: state.tune,
status: state.status,
config: state.config,
loadedLogs: state.logs,
});
const margin = 30;
const sidebarWidth = 250;
const minCanvasHeightInner = 600;
const Log = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loadedLogs: Logs }) => {
const Log = ({
ui,
config,
tune,
loadedLogs,
}: {
ui: UIState,
tune: TuneType,
config: Config,
loadedLogs: Logs,
}) => {
const { lg } = useBreakpoint();
const { Sider } = Layout;
const [progress, setProgress] = useState(0);
@ -215,8 +230,7 @@ const Log = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loadedLo
{fields.map((field) => (
<Row key={field.name}>
<Checkbox key={field.name} value={field.name}>
{field.label}
{/* {field.units && ` (${field.units})`} */}
{isExpression(field.label) ? stripExpression(field.label) : field.label}
</Checkbox>
</Row>
))}
@ -230,8 +244,7 @@ const Log = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loadedLo
{fields.map((field) => (
<Row key={field.name}>
<Checkbox key={field.name} value={field.name}>
{field.label}
{/* {field.units && ` (${field.units})`} */}
{isExpression(field.label) ? stripExpression(field.label) : field.label}
</Checkbox>
</Row>
))}

View File

@ -9,7 +9,11 @@ import {
Link,
} from 'react-router-dom';
import PerfectScrollbar from 'react-perfect-scrollbar';
import { useCallback } from 'react';
import {
useCallback,
useEffect,
useState,
} from 'react';
import {
Config as ConfigType,
Menus as MenusType,
@ -79,9 +83,10 @@ const SideBar = ({
category: main,
dialog: sub,
}), []);
const [menus, setMenus] = useState<any[]>([]);
const menusList = useCallback((menus: MenusType) => (
Object.keys(menus).map((menuName: string) => {
const menusList = useCallback((types: MenusType) => (
Object.keys(types).map((menuName: string) => {
if (SKIP_MENUS.includes(menuName)) {
return null;
}
@ -90,9 +95,9 @@ const SideBar = ({
<SubMenu
key={`/${menuName}`}
icon={<Icon name={menuName} />}
title={menus[menuName].title}
title={types[menuName].title}
>
{Object.keys(menus[menuName].subMenus).map((subMenuName: string) => {
{Object.keys(types[menuName].subMenus).map((subMenuName: string) => {
if (subMenuName === 'std_separator') {
return <Menu.Divider key={buildLink(menuName, subMenuName)} />;
}
@ -101,10 +106,9 @@ const SideBar = ({
return null;
}
const subMenu = menus[menuName].subMenus[subMenuName];
const subMenu = types[menuName].subMenus[subMenuName];
let enabled = true;
// TODO: optimize this!
if (subMenu.condition) {
enabled = checkCondition(subMenu.condition);
}
@ -124,6 +128,12 @@ const SideBar = ({
})
), [buildLink, checkCondition]);
useEffect(() => {
if (Object.keys(tune.constants).length) {
setMenus(menusList(config.menus));
}
}, [config.menus, menusList, tune.constants]);
if (!isConfigReady) {
return (
<Sider {...siderProps} className="app-sidebar" >
@ -148,7 +158,7 @@ const SideBar = ({
style={{ height: '100%' }}
key={matchedPath.url}
>
{Object.keys(tune.constants).length && menusList(config.menus)}
{menus}
</Menu>
</PerfectScrollbar>
</Sider>