Skip to content

Commit d1f4184

Browse files
authored
perf: reduce DBSearchPage and DBChartExplorerPage rerenders from typing in search input (#924)
1 parent b427ae3 commit d1f4184

File tree

9 files changed

+122
-62
lines changed

9 files changed

+122
-62
lines changed

.changeset/breezy-pandas-behave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/app": patch
3+
---
4+
5+
perf: improve performance on chart page and search page

packages/app/src/DBSearchPage.tsx

Lines changed: 74 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
FormEvent,
3+
FormEventHandler,
34
useCallback,
45
useEffect,
56
useMemo,
@@ -964,14 +965,18 @@ function DBSearchPage() {
964965

965966
const { data: aliasMap } = useAliasMapFromChartConfig(dbSqlRowTableConfig);
966967

967-
const aliasWith = Object.entries(aliasMap ?? {}).map(([key, value]) => ({
968-
name: key,
969-
sql: {
970-
sql: value,
971-
params: {},
972-
},
973-
isSubquery: false,
974-
}));
968+
const aliasWith = useMemo(
969+
() =>
970+
Object.entries(aliasMap ?? {}).map(([key, value]) => ({
971+
name: key,
972+
sql: {
973+
sql: value,
974+
params: {},
975+
},
976+
isSubquery: false,
977+
})),
978+
[aliasMap],
979+
);
975980

976981
const histogramTimeChartConfig = useMemo(() => {
977982
if (chartConfig == null) {
@@ -1006,6 +1011,60 @@ function DBSearchPage() {
10061011
};
10071012
}, [chartConfig, searchedSource, aliasWith, searchedTimeRange]);
10081013

1014+
const onFormSubmit = useCallback<FormEventHandler<HTMLFormElement>>(
1015+
e => {
1016+
e.preventDefault();
1017+
onSubmit();
1018+
return false;
1019+
},
1020+
[onSubmit],
1021+
);
1022+
1023+
const handleTimeRangeSelect = useCallback(
1024+
(d1: Date, d2: Date) => {
1025+
onTimeRangeSelect(d1, d2);
1026+
setIsLive(false);
1027+
},
1028+
[onTimeRangeSelect],
1029+
);
1030+
1031+
const onTimeChartError = useCallback(
1032+
(error: Error | ClickHouseQueryError) =>
1033+
setQueryErrors(prev => ({
1034+
...prev,
1035+
DBTimeChart: error,
1036+
})),
1037+
[setQueryErrors],
1038+
);
1039+
1040+
const filtersChartConfig = useMemo<ChartConfigWithDateRange>(() => {
1041+
const overrides = {
1042+
orderBy: undefined,
1043+
dateRange: searchedTimeRange,
1044+
with: aliasWith,
1045+
} as const;
1046+
return chartConfig
1047+
? {
1048+
...chartConfig,
1049+
...overrides,
1050+
}
1051+
: {
1052+
timestampValueExpression: '',
1053+
connection: '',
1054+
from: {
1055+
databaseName: '',
1056+
tableName: '',
1057+
},
1058+
where: '',
1059+
select: '',
1060+
...overrides,
1061+
};
1062+
}, [chartConfig, searchedTimeRange, aliasWith]);
1063+
1064+
const openNewSourceModal = useCallback(() => {
1065+
setNewSourceModalOpened(true);
1066+
}, []);
1067+
10091068
return (
10101069
<Flex direction="column" h="100vh" style={{ overflow: 'hidden' }}>
10111070
{!IS_LOCAL_MODE && isAlertModalOpen && (
@@ -1017,13 +1076,7 @@ function DBSearchPage() {
10171076
/>
10181077
)}
10191078
<OnboardingModal />
1020-
<form
1021-
onSubmit={e => {
1022-
e.preventDefault();
1023-
onSubmit();
1024-
return false;
1025-
}}
1026-
>
1079+
<form onSubmit={onFormSubmit}>
10271080
{/* <DevTool control={control} /> */}
10281081
<Flex gap="sm" px="sm" pt="sm" wrap="nowrap">
10291082
<Group gap="4px" wrap="nowrap">
@@ -1032,9 +1085,7 @@ function DBSearchPage() {
10321085
size="xs"
10331086
control={control}
10341087
name="source"
1035-
onCreate={() => {
1036-
setNewSourceModalOpened(true);
1037-
}}
1088+
onCreate={openNewSourceModal}
10381089
/>
10391090
<ActionIcon
10401091
variant="subtle"
@@ -1297,12 +1348,7 @@ function DBSearchPage() {
12971348
isLive={isLive}
12981349
analysisMode={analysisMode}
12991350
setAnalysisMode={setAnalysisMode}
1300-
chartConfig={{
1301-
...chartConfig,
1302-
orderBy: undefined,
1303-
dateRange: searchedTimeRange,
1304-
with: aliasWith,
1305-
}}
1351+
chartConfig={filtersChartConfig}
13061352
sourceId={inputSourceObj?.id}
13071353
showDelta={!!searchedSource?.durationExpression}
13081354
{...searchFilters}
@@ -1344,16 +1390,8 @@ function DBSearchPage() {
13441390
enabled={isReady}
13451391
showDisplaySwitcher={false}
13461392
queryKeyPrefix={QUERY_KEY_PREFIX}
1347-
onTimeRangeSelect={(d1, d2) => {
1348-
onTimeRangeSelect(d1, d2);
1349-
setIsLive(false);
1350-
}}
1351-
onError={error =>
1352-
setQueryErrors(prev => ({
1353-
...prev,
1354-
DBTimeChart: error,
1355-
}))
1356-
}
1393+
onTimeRangeSelect={handleTimeRangeSelect}
1394+
onError={onTimeChartError}
13571395
/>
13581396
</Box>
13591397
)}
@@ -1464,16 +1502,8 @@ function DBSearchPage() {
14641502
enabled={isReady}
14651503
showDisplaySwitcher={false}
14661504
queryKeyPrefix={QUERY_KEY_PREFIX}
1467-
onTimeRangeSelect={(d1, d2) => {
1468-
onTimeRangeSelect(d1, d2);
1469-
setIsLive(false);
1470-
}}
1471-
onError={error =>
1472-
setQueryErrors(prev => ({
1473-
...prev,
1474-
DBTimeChart: error,
1475-
}))
1476-
}
1505+
onTimeRangeSelect={handleTimeRangeSelect}
1506+
onError={onTimeChartError}
14771507
/>
14781508
</Box>
14791509
)}

packages/app/src/GranularityPicker.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { memo } from 'react';
12
import { useController, UseControllerProps } from 'react-hook-form';
23

34
import { Granularity } from './ChartUtils';
@@ -63,7 +64,9 @@ export default function GranularityPicker({
6364
);
6465
}
6566

66-
export function GranularityPickerControlled(props: UseControllerProps<any>) {
67+
export function GranularityPickerControlledComponent(
68+
props: UseControllerProps<any>,
69+
) {
6770
const {
6871
field,
6972
fieldState: { invalid, isTouched, isDirty },
@@ -72,3 +75,7 @@ export function GranularityPickerControlled(props: UseControllerProps<any>) {
7275

7376
return <GranularityPicker value={field.value} onChange={field.onChange} />;
7477
}
78+
79+
export const GranularityPickerControlled = memo(
80+
GranularityPickerControlledComponent,
81+
);

packages/app/src/components/DBEditTimeChartForm.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useMemo, useState } from 'react';
1+
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
22
import {
33
Control,
44
Controller,
@@ -106,7 +106,7 @@ const NumberFormatInputControlled = ({
106106
);
107107
};
108108

109-
function ChartSeriesEditor({
109+
function ChartSeriesEditorComponent({
110110
control,
111111
databaseName,
112112
dateRange,
@@ -124,9 +124,9 @@ function ChartSeriesEditor({
124124
databaseName: string;
125125
dateRange?: DateRange['dateRange'];
126126
connectionId?: string;
127-
index?: number;
127+
index: number;
128128
namePrefix: string;
129-
onRemoveSeries: () => void;
129+
onRemoveSeries: (index: number) => void;
130130
onSubmit: () => void;
131131
setValue: UseFormSetValue<any>;
132132
showGroupBy: boolean;
@@ -168,7 +168,7 @@ function ChartSeriesEditor({
168168
variant="subtle"
169169
color="gray"
170170
size="xs"
171-
onClick={() => onRemoveSeries()}
171+
onClick={() => onRemoveSeries(index)}
172172
>
173173
<i className="bi bi-trash me-2" />
174174
Remove Series
@@ -286,6 +286,7 @@ function ChartSeriesEditor({
286286
</>
287287
);
288288
}
289+
const ChartSeriesEditor = memo(ChartSeriesEditorComponent);
289290

290291
// Autocomplete can focus on column/map keys
291292

@@ -337,7 +338,11 @@ export default function EditTimeChartForm({
337338
resolver: zodResolver(zSavedChartConfig),
338339
});
339340

340-
const { fields, append, remove } = useFieldArray({
341+
const {
342+
fields,
343+
append,
344+
remove: removeSeries,
345+
} = useFieldArray({
341346
control: control as Control<SavedChartConfigWithSelectArray>,
342347
name: 'select',
343348
});
@@ -604,7 +609,7 @@ export default function EditTimeChartForm({
604609
index={index}
605610
key={field.id}
606611
namePrefix={`select.${index}.`}
607-
onRemoveSeries={() => remove(index)}
612+
onRemoveSeries={removeSeries}
608613
onSubmit={onSubmit}
609614
setValue={setValue}
610615
connectionId={tableSource?.connection}

packages/app/src/components/DBRowTable.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,7 @@ export function selectColumnMapWithoutAdditionalKeys(
898898
);
899899
}
900900

901-
export function DBSqlRowTable({
901+
function DBSqlRowTableComponent({
902902
config,
903903
sourceId,
904904
onError,
@@ -977,7 +977,10 @@ export function DBSqlRowTable({
977977
});
978978
}, [data, objectTypeColumns, columnMap]);
979979

980-
const aliasMap = chSqlToAliasMap(data?.chSql ?? { sql: '', params: {} });
980+
const aliasMap = useMemo(
981+
() => chSqlToAliasMap(data?.chSql ?? { sql: '', params: {} }),
982+
[data],
983+
);
981984

982985
const getRowWhere = useRowWhere({ meta: data?.meta, aliasMap });
983986

@@ -1110,3 +1113,4 @@ export function DBSqlRowTable({
11101113
</>
11111114
);
11121115
}
1116+
export const DBSqlRowTable = memo(DBSqlRowTableComponent);

packages/app/src/components/DBSearchPageFilters.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useMemo, useState } from 'react';
1+
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
22
import { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
33
import {
44
Box,
@@ -351,7 +351,7 @@ export const FilterGroup = ({
351351
);
352352
};
353353

354-
export const DBSearchPageFilters = ({
354+
const DBSearchPageFiltersComponent = ({
355355
filters: filterState,
356356
clearAllFilters,
357357
clearFilter,
@@ -671,3 +671,5 @@ export const DBSearchPageFilters = ({
671671
</Box>
672672
);
673673
};
674+
675+
export const DBSearchPageFilters = memo(DBSearchPageFiltersComponent);

packages/app/src/components/DBTimeChart.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMemo, useState } from 'react';
1+
import { memo, useMemo, useState } from 'react';
22
import Link from 'next/link';
33
import cx from 'classnames';
44
import { add } from 'date-fns';
@@ -22,7 +22,7 @@ import { SQLPreview } from './ChartSQLPreview';
2222

2323
// TODO: Support clicking in to view matched events
2424

25-
export function DBTimeChart({
25+
function DBTimeChartComponent({
2626
config,
2727
enabled = true,
2828
logReferenceTimestamp,
@@ -322,3 +322,5 @@ export function DBTimeChart({
322322
</div>
323323
);
324324
}
325+
326+
export const DBTimeChart = memo(DBTimeChartComponent);

packages/app/src/components/SQLInlineEditor.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
1+
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
22
import { useController, UseControllerProps } from 'react-hook-form';
33
import { useHotkeys } from 'react-hotkeys-hook';
44
import {
@@ -350,7 +350,7 @@ export default function SQLInlineEditor({
350350
);
351351
}
352352

353-
export function SQLInlineEditorControlled({
353+
function SQLInlineEditorControlledComponent({
354354
placeholder,
355355
filterField,
356356
additionalSuggestions,
@@ -381,3 +381,6 @@ export function SQLInlineEditorControlled({
381381
/>
382382
);
383383
}
384+
export const SQLInlineEditorControlled = memo(
385+
SQLInlineEditorControlledComponent,
386+
);

packages/app/src/components/SourceSelect.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { useMemo } from 'react';
1+
import { memo, useMemo } from 'react';
22
import { UseControllerProps } from 'react-hook-form';
33

44
import SelectControlled from '@/components/SelectControlled';
55
import { HDX_LOCAL_DEFAULT_SOURCES } from '@/config';
66
import { useSources } from '@/source';
77

8-
export function SourceSelectControlled({
8+
function SourceSelectControlledComponent({
99
size,
1010
onCreate,
1111
...props
@@ -46,3 +46,5 @@ export function SourceSelectControlled({
4646
/>
4747
);
4848
}
49+
50+
export const SourceSelectControlled = memo(SourceSelectControlledComponent);

0 commit comments

Comments
 (0)