mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
perf: reduce DBSearchPage and DBChartExplorerPage rerenders from typing in search input (#924)
This commit is contained in:
parent
b427ae3526
commit
d1f4184536
9 changed files with 122 additions and 62 deletions
5
.changeset/breezy-pandas-behave.md
Normal file
5
.changeset/breezy-pandas-behave.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@hyperdx/app": patch
|
||||
---
|
||||
|
||||
perf: improve performance on chart page and search page
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
FormEvent,
|
||||
FormEventHandler,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
|
|
@ -964,14 +965,18 @@ function DBSearchPage() {
|
|||
|
||||
const { data: aliasMap } = useAliasMapFromChartConfig(dbSqlRowTableConfig);
|
||||
|
||||
const aliasWith = Object.entries(aliasMap ?? {}).map(([key, value]) => ({
|
||||
name: key,
|
||||
sql: {
|
||||
sql: value,
|
||||
params: {},
|
||||
},
|
||||
isSubquery: false,
|
||||
}));
|
||||
const aliasWith = useMemo(
|
||||
() =>
|
||||
Object.entries(aliasMap ?? {}).map(([key, value]) => ({
|
||||
name: key,
|
||||
sql: {
|
||||
sql: value,
|
||||
params: {},
|
||||
},
|
||||
isSubquery: false,
|
||||
})),
|
||||
[aliasMap],
|
||||
);
|
||||
|
||||
const histogramTimeChartConfig = useMemo(() => {
|
||||
if (chartConfig == null) {
|
||||
|
|
@ -1006,6 +1011,60 @@ function DBSearchPage() {
|
|||
};
|
||||
}, [chartConfig, searchedSource, aliasWith, searchedTimeRange]);
|
||||
|
||||
const onFormSubmit = useCallback<FormEventHandler<HTMLFormElement>>(
|
||||
e => {
|
||||
e.preventDefault();
|
||||
onSubmit();
|
||||
return false;
|
||||
},
|
||||
[onSubmit],
|
||||
);
|
||||
|
||||
const handleTimeRangeSelect = useCallback(
|
||||
(d1: Date, d2: Date) => {
|
||||
onTimeRangeSelect(d1, d2);
|
||||
setIsLive(false);
|
||||
},
|
||||
[onTimeRangeSelect],
|
||||
);
|
||||
|
||||
const onTimeChartError = useCallback(
|
||||
(error: Error | ClickHouseQueryError) =>
|
||||
setQueryErrors(prev => ({
|
||||
...prev,
|
||||
DBTimeChart: error,
|
||||
})),
|
||||
[setQueryErrors],
|
||||
);
|
||||
|
||||
const filtersChartConfig = useMemo<ChartConfigWithDateRange>(() => {
|
||||
const overrides = {
|
||||
orderBy: undefined,
|
||||
dateRange: searchedTimeRange,
|
||||
with: aliasWith,
|
||||
} as const;
|
||||
return chartConfig
|
||||
? {
|
||||
...chartConfig,
|
||||
...overrides,
|
||||
}
|
||||
: {
|
||||
timestampValueExpression: '',
|
||||
connection: '',
|
||||
from: {
|
||||
databaseName: '',
|
||||
tableName: '',
|
||||
},
|
||||
where: '',
|
||||
select: '',
|
||||
...overrides,
|
||||
};
|
||||
}, [chartConfig, searchedTimeRange, aliasWith]);
|
||||
|
||||
const openNewSourceModal = useCallback(() => {
|
||||
setNewSourceModalOpened(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Flex direction="column" h="100vh" style={{ overflow: 'hidden' }}>
|
||||
{!IS_LOCAL_MODE && isAlertModalOpen && (
|
||||
|
|
@ -1017,13 +1076,7 @@ function DBSearchPage() {
|
|||
/>
|
||||
)}
|
||||
<OnboardingModal />
|
||||
<form
|
||||
onSubmit={e => {
|
||||
e.preventDefault();
|
||||
onSubmit();
|
||||
return false;
|
||||
}}
|
||||
>
|
||||
<form onSubmit={onFormSubmit}>
|
||||
{/* <DevTool control={control} /> */}
|
||||
<Flex gap="sm" px="sm" pt="sm" wrap="nowrap">
|
||||
<Group gap="4px" wrap="nowrap">
|
||||
|
|
@ -1032,9 +1085,7 @@ function DBSearchPage() {
|
|||
size="xs"
|
||||
control={control}
|
||||
name="source"
|
||||
onCreate={() => {
|
||||
setNewSourceModalOpened(true);
|
||||
}}
|
||||
onCreate={openNewSourceModal}
|
||||
/>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
|
|
@ -1297,12 +1348,7 @@ function DBSearchPage() {
|
|||
isLive={isLive}
|
||||
analysisMode={analysisMode}
|
||||
setAnalysisMode={setAnalysisMode}
|
||||
chartConfig={{
|
||||
...chartConfig,
|
||||
orderBy: undefined,
|
||||
dateRange: searchedTimeRange,
|
||||
with: aliasWith,
|
||||
}}
|
||||
chartConfig={filtersChartConfig}
|
||||
sourceId={inputSourceObj?.id}
|
||||
showDelta={!!searchedSource?.durationExpression}
|
||||
{...searchFilters}
|
||||
|
|
@ -1344,16 +1390,8 @@ function DBSearchPage() {
|
|||
enabled={isReady}
|
||||
showDisplaySwitcher={false}
|
||||
queryKeyPrefix={QUERY_KEY_PREFIX}
|
||||
onTimeRangeSelect={(d1, d2) => {
|
||||
onTimeRangeSelect(d1, d2);
|
||||
setIsLive(false);
|
||||
}}
|
||||
onError={error =>
|
||||
setQueryErrors(prev => ({
|
||||
...prev,
|
||||
DBTimeChart: error,
|
||||
}))
|
||||
}
|
||||
onTimeRangeSelect={handleTimeRangeSelect}
|
||||
onError={onTimeChartError}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
|
@ -1464,16 +1502,8 @@ function DBSearchPage() {
|
|||
enabled={isReady}
|
||||
showDisplaySwitcher={false}
|
||||
queryKeyPrefix={QUERY_KEY_PREFIX}
|
||||
onTimeRangeSelect={(d1, d2) => {
|
||||
onTimeRangeSelect(d1, d2);
|
||||
setIsLive(false);
|
||||
}}
|
||||
onError={error =>
|
||||
setQueryErrors(prev => ({
|
||||
...prev,
|
||||
DBTimeChart: error,
|
||||
}))
|
||||
}
|
||||
onTimeRangeSelect={handleTimeRangeSelect}
|
||||
onError={onTimeChartError}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { memo } from 'react';
|
||||
import { useController, UseControllerProps } from 'react-hook-form';
|
||||
|
||||
import { Granularity } from './ChartUtils';
|
||||
|
|
@ -63,7 +64,9 @@ export default function GranularityPicker({
|
|||
);
|
||||
}
|
||||
|
||||
export function GranularityPickerControlled(props: UseControllerProps<any>) {
|
||||
export function GranularityPickerControlledComponent(
|
||||
props: UseControllerProps<any>,
|
||||
) {
|
||||
const {
|
||||
field,
|
||||
fieldState: { invalid, isTouched, isDirty },
|
||||
|
|
@ -72,3 +75,7 @@ export function GranularityPickerControlled(props: UseControllerProps<any>) {
|
|||
|
||||
return <GranularityPicker value={field.value} onChange={field.onChange} />;
|
||||
}
|
||||
|
||||
export const GranularityPickerControlled = memo(
|
||||
GranularityPickerControlledComponent,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Control,
|
||||
Controller,
|
||||
|
|
@ -106,7 +106,7 @@ const NumberFormatInputControlled = ({
|
|||
);
|
||||
};
|
||||
|
||||
function ChartSeriesEditor({
|
||||
function ChartSeriesEditorComponent({
|
||||
control,
|
||||
databaseName,
|
||||
dateRange,
|
||||
|
|
@ -124,9 +124,9 @@ function ChartSeriesEditor({
|
|||
databaseName: string;
|
||||
dateRange?: DateRange['dateRange'];
|
||||
connectionId?: string;
|
||||
index?: number;
|
||||
index: number;
|
||||
namePrefix: string;
|
||||
onRemoveSeries: () => void;
|
||||
onRemoveSeries: (index: number) => void;
|
||||
onSubmit: () => void;
|
||||
setValue: UseFormSetValue<any>;
|
||||
showGroupBy: boolean;
|
||||
|
|
@ -168,7 +168,7 @@ function ChartSeriesEditor({
|
|||
variant="subtle"
|
||||
color="gray"
|
||||
size="xs"
|
||||
onClick={() => onRemoveSeries()}
|
||||
onClick={() => onRemoveSeries(index)}
|
||||
>
|
||||
<i className="bi bi-trash me-2" />
|
||||
Remove Series
|
||||
|
|
@ -286,6 +286,7 @@ function ChartSeriesEditor({
|
|||
</>
|
||||
);
|
||||
}
|
||||
const ChartSeriesEditor = memo(ChartSeriesEditorComponent);
|
||||
|
||||
// Autocomplete can focus on column/map keys
|
||||
|
||||
|
|
@ -337,7 +338,11 @@ export default function EditTimeChartForm({
|
|||
resolver: zodResolver(zSavedChartConfig),
|
||||
});
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
const {
|
||||
fields,
|
||||
append,
|
||||
remove: removeSeries,
|
||||
} = useFieldArray({
|
||||
control: control as Control<SavedChartConfigWithSelectArray>,
|
||||
name: 'select',
|
||||
});
|
||||
|
|
@ -604,7 +609,7 @@ export default function EditTimeChartForm({
|
|||
index={index}
|
||||
key={field.id}
|
||||
namePrefix={`select.${index}.`}
|
||||
onRemoveSeries={() => remove(index)}
|
||||
onRemoveSeries={removeSeries}
|
||||
onSubmit={onSubmit}
|
||||
setValue={setValue}
|
||||
connectionId={tableSource?.connection}
|
||||
|
|
|
|||
|
|
@ -898,7 +898,7 @@ export function selectColumnMapWithoutAdditionalKeys(
|
|||
);
|
||||
}
|
||||
|
||||
export function DBSqlRowTable({
|
||||
function DBSqlRowTableComponent({
|
||||
config,
|
||||
sourceId,
|
||||
onError,
|
||||
|
|
@ -977,7 +977,10 @@ export function DBSqlRowTable({
|
|||
});
|
||||
}, [data, objectTypeColumns, columnMap]);
|
||||
|
||||
const aliasMap = chSqlToAliasMap(data?.chSql ?? { sql: '', params: {} });
|
||||
const aliasMap = useMemo(
|
||||
() => chSqlToAliasMap(data?.chSql ?? { sql: '', params: {} }),
|
||||
[data],
|
||||
);
|
||||
|
||||
const getRowWhere = useRowWhere({ meta: data?.meta, aliasMap });
|
||||
|
||||
|
|
@ -1110,3 +1113,4 @@ export function DBSqlRowTable({
|
|||
</>
|
||||
);
|
||||
}
|
||||
export const DBSqlRowTable = memo(DBSqlRowTableComponent);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
|
||||
import {
|
||||
Box,
|
||||
|
|
@ -351,7 +351,7 @@ export const FilterGroup = ({
|
|||
);
|
||||
};
|
||||
|
||||
export const DBSearchPageFilters = ({
|
||||
const DBSearchPageFiltersComponent = ({
|
||||
filters: filterState,
|
||||
clearAllFilters,
|
||||
clearFilter,
|
||||
|
|
@ -671,3 +671,5 @@ export const DBSearchPageFilters = ({
|
|||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const DBSearchPageFilters = memo(DBSearchPageFiltersComponent);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useMemo, useState } from 'react';
|
||||
import { memo, useMemo, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import cx from 'classnames';
|
||||
import { add } from 'date-fns';
|
||||
|
|
@ -22,7 +22,7 @@ import { SQLPreview } from './ChartSQLPreview';
|
|||
|
||||
// TODO: Support clicking in to view matched events
|
||||
|
||||
export function DBTimeChart({
|
||||
function DBTimeChartComponent({
|
||||
config,
|
||||
enabled = true,
|
||||
logReferenceTimestamp,
|
||||
|
|
@ -322,3 +322,5 @@ export function DBTimeChart({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const DBTimeChart = memo(DBTimeChartComponent);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useController, UseControllerProps } from 'react-hook-form';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import {
|
||||
|
|
@ -350,7 +350,7 @@ export default function SQLInlineEditor({
|
|||
);
|
||||
}
|
||||
|
||||
export function SQLInlineEditorControlled({
|
||||
function SQLInlineEditorControlledComponent({
|
||||
placeholder,
|
||||
filterField,
|
||||
additionalSuggestions,
|
||||
|
|
@ -381,3 +381,6 @@ export function SQLInlineEditorControlled({
|
|||
/>
|
||||
);
|
||||
}
|
||||
export const SQLInlineEditorControlled = memo(
|
||||
SQLInlineEditorControlledComponent,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { UseControllerProps } from 'react-hook-form';
|
||||
|
||||
import SelectControlled from '@/components/SelectControlled';
|
||||
import { HDX_LOCAL_DEFAULT_SOURCES } from '@/config';
|
||||
import { useSources } from '@/source';
|
||||
|
||||
export function SourceSelectControlled({
|
||||
function SourceSelectControlledComponent({
|
||||
size,
|
||||
onCreate,
|
||||
...props
|
||||
|
|
@ -46,3 +46,5 @@ export function SourceSelectControlled({
|
|||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const SourceSelectControlled = memo(SourceSelectControlledComponent);
|
||||
|
|
|
|||
Loading…
Reference in a new issue