mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Tracing: Performance optimization (#23474)
* Add integration with Jeager Add Jaeger datasource and modify derived fields in loki to allow for opening a trace in Jager in separate split. Modifies build so that this branch docker images are pushed to docker hub Add a traceui dir with docker-compose and provision files for demoing.:wq * Enable docker logger plugin to send logs to loki * Add placeholder zipkin datasource * Fixed rebase issues, added enhanceDataFrame to non-legacy code path * Trace selector for jaeger query field * Fix logs default mode for Loki * Fix loading jaeger query field services on split * Updated grafana image in traceui/compose file * Fix prettier error * Hide behind feature flag, clean up unused code. * Fix tests * Fix tests * Cleanup code and review feedback * Remove traceui directory * Remove circle build changes * Fix feature toggles object * Fix merge issues * Add trace ui in Explore * WIP * WIP * WIP * Make jaeger datasource return trace data instead of link * Allow js in jest tests * Return data from Jaeger datasource * Take yarn.lock from master * Fix missing component * Update yarn lock * Fix some ts and lint errors * Fix merge * Fix type errors * Make tests pass again * Add tests * Fix es5 compatibility * Add header with minimap * Fix sizing issue due to column resizer handle * Fix issues with sizing, search functionality, duplicate react, tests * Refactor TraceView component, fix tests * Fix type errors * Add dark theme styling * Add tests for hooks * More color changes * Fix tests to deal with additional theme wrappers. * Add memoization * Fix duplicate identifier Co-authored-by: David Kaltschmidt <david.kaltschmidt@gmail.com>
This commit is contained in:
@@ -10,8 +10,14 @@ datasources:
|
||||
jsonData:
|
||||
derivedFields:
|
||||
- name: "traceID"
|
||||
matcherRegex: "traceID=(\\w+)"
|
||||
matcherRegex: "trace_id=(\\w+)"
|
||||
url: "$${__value.raw}"
|
||||
datasourceName: "Jaeger"
|
||||
datasourceUid: jaeger1234
|
||||
|
||||
- name: jaeger-derived-test
|
||||
type: jaeger
|
||||
uid: jaeger1234
|
||||
access: proxy
|
||||
url: http://localhost:16686
|
||||
editable: false
|
||||
|
||||
|
||||
@@ -50,18 +50,22 @@ export function ThemeConsumer(props: ThemeConsumerProps) {
|
||||
return (
|
||||
<ThemeContext.Consumer>
|
||||
{(value: ThemeOptions | undefined) => {
|
||||
const mergedTheme: Theme = value
|
||||
? {
|
||||
...defaultTheme,
|
||||
...value,
|
||||
}
|
||||
: defaultTheme;
|
||||
return props.children(mergedTheme);
|
||||
const theme = memoizedThemeMerge(value);
|
||||
return props.children(theme);
|
||||
}}
|
||||
</ThemeContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
const memoizedThemeMerge = memoizeOne(value => {
|
||||
return value
|
||||
? {
|
||||
...defaultTheme,
|
||||
...value,
|
||||
}
|
||||
: defaultTheme;
|
||||
});
|
||||
|
||||
type WrappedWithThemeComponent<Props> = React.ComponentType<Omit<Props, 'theme'>> & {
|
||||
wrapped: React.ComponentType<Props>;
|
||||
};
|
||||
|
||||
@@ -34,6 +34,7 @@ import { getTraceLinks } from '../model/link-patterns';
|
||||
import ExternalLinks from '../common/ExternalLinks';
|
||||
import { createStyle } from '../Theme';
|
||||
import { uTxMuted } from '../uberUtilityStyles';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
const getStyles = createStyle((theme: Theme) => {
|
||||
const TracePageHeaderOverviewItemValueDetail = css`
|
||||
@@ -222,7 +223,7 @@ export default function TracePageHeader(props: TracePageHeaderEmbedProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const links = getTraceLinks(trace);
|
||||
const links = useMemo(() => getTraceLinks(trace), [trace]);
|
||||
|
||||
const summaryItems =
|
||||
!hideSummary &&
|
||||
|
||||
@@ -24,6 +24,7 @@ import { TNil } from '../types';
|
||||
import { UIButton, UIInputGroup } from '../uiElementsContext';
|
||||
import { createStyle } from '../Theme';
|
||||
import { ubFlexAuto, ubJustifyEnd } from '../uberUtilityStyles';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const getStyles = createStyle(() => {
|
||||
return {
|
||||
@@ -71,7 +72,7 @@ type TracePageSearchBarProps = {
|
||||
hideSearchButtons?: boolean;
|
||||
};
|
||||
|
||||
export default function TracePageSearchBar(props: TracePageSearchBarProps) {
|
||||
export default memo(function TracePageSearchBar(props: TracePageSearchBarProps) {
|
||||
const {
|
||||
clearSearch,
|
||||
focusUiFindMatches,
|
||||
@@ -141,4 +142,4 @@ export default function TracePageSearchBar(props: TracePageSearchBarProps) {
|
||||
</UIInputGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
KeyValuePair,
|
||||
Link,
|
||||
@@ -50,27 +50,36 @@ export function TraceView(props: Props) {
|
||||
*/
|
||||
const [slim, setSlim] = useState(false);
|
||||
|
||||
const traceProp = transformTraceData(props.trace);
|
||||
const traceProp = useMemo(() => transformTraceData(props.trace), [props.trace]);
|
||||
const { search, setSearch, spanFindMatches } = useSearch(traceProp?.spans);
|
||||
|
||||
const theme = useTheme();
|
||||
const traceTheme = useMemo(() => ({ type: theme.isDark ? ThemeType.Dark : ThemeType.Light }), [theme]);
|
||||
const traceTimeline: TTraceTimeline = useMemo(
|
||||
() => ({
|
||||
childrenHiddenIDs,
|
||||
detailStates,
|
||||
hoverIndentGuideIds,
|
||||
shouldScrollToFirstUiFindMatch: false,
|
||||
spanNameColumnWidth,
|
||||
traceID: traceProp.traceID,
|
||||
}),
|
||||
[childrenHiddenIDs, detailStates, hoverIndentGuideIds, spanNameColumnWidth, traceProp.traceID]
|
||||
);
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
value={{
|
||||
type: theme.isDark ? ThemeType.Dark : ThemeType.Light,
|
||||
}}
|
||||
>
|
||||
<ThemeProvider value={traceTheme}>
|
||||
<UIElementsContext.Provider value={UIElements}>
|
||||
<TracePageHeader
|
||||
canCollapse={true}
|
||||
clearSearch={() => {}}
|
||||
focusUiFindMatches={() => {}}
|
||||
clearSearch={useCallback(() => {}, [])}
|
||||
focusUiFindMatches={useCallback(() => {}, [])}
|
||||
hideMap={false}
|
||||
hideSummary={false}
|
||||
nextResult={() => {}}
|
||||
onSlimViewClicked={() => setSlim(!slim)}
|
||||
onTraceGraphViewClicked={() => {}}
|
||||
prevResult={() => {}}
|
||||
nextResult={useCallback(() => {}, [])}
|
||||
onSlimViewClicked={useCallback(() => setSlim(!slim), [])}
|
||||
onTraceGraphViewClicked={useCallback(() => {}, [])}
|
||||
prevResult={useCallback(() => {}, [])}
|
||||
resultCount={0}
|
||||
slimView={slim}
|
||||
textFilter={null}
|
||||
@@ -84,32 +93,23 @@ export function TraceView(props: Props) {
|
||||
hideSearchButtons={true}
|
||||
/>
|
||||
<TraceTimelineViewer
|
||||
registerAccessors={() => {}}
|
||||
scrollToFirstVisibleSpan={() => {}}
|
||||
registerAccessors={useCallback(() => {}, [])}
|
||||
scrollToFirstVisibleSpan={useCallback(() => {}, [])}
|
||||
findMatchesIDs={spanFindMatches}
|
||||
trace={traceProp}
|
||||
traceTimeline={
|
||||
{
|
||||
childrenHiddenIDs,
|
||||
detailStates,
|
||||
hoverIndentGuideIds,
|
||||
shouldScrollToFirstUiFindMatch: false,
|
||||
spanNameColumnWidth,
|
||||
traceID: '50b96206cf81dd64',
|
||||
} as TTraceTimeline
|
||||
}
|
||||
traceTimeline={traceTimeline}
|
||||
updateNextViewRangeTime={updateNextViewRangeTime}
|
||||
updateViewRangeTime={updateViewRangeTime}
|
||||
viewRange={viewRange}
|
||||
focusSpan={() => {}}
|
||||
createLinkToExternalSpan={() => ''}
|
||||
focusSpan={useCallback(() => {}, [])}
|
||||
createLinkToExternalSpan={useCallback(() => '', [])}
|
||||
setSpanNameColumnWidth={setSpanNameColumnWidth}
|
||||
collapseAll={collapseAll}
|
||||
collapseOne={collapseOne}
|
||||
expandAll={expandAll}
|
||||
expandOne={expandOne}
|
||||
childrenToggle={childrenToggle}
|
||||
clearShouldScrollToFirstUiFindMatch={() => {}}
|
||||
clearShouldScrollToFirstUiFindMatch={useCallback(() => {}, [])}
|
||||
detailLogItemToggle={detailLogItemToggle}
|
||||
detailLogsToggle={detailLogsToggle}
|
||||
detailWarningsToggle={detailWarningsToggle}
|
||||
@@ -117,10 +117,10 @@ export function TraceView(props: Props) {
|
||||
detailProcessToggle={detailProcessToggle}
|
||||
detailTagsToggle={detailTagsToggle}
|
||||
detailToggle={toggleDetail}
|
||||
setTrace={(trace: Trace | null, uiFind: string | null) => {}}
|
||||
setTrace={useCallback((trace: Trace | null, uiFind: string | null) => {}, [])}
|
||||
addHoverIndentGuideId={addHoverIndentGuideId}
|
||||
removeHoverIndentGuideId={removeHoverIndentGuideId}
|
||||
linksGetter={(span: Span, items: KeyValuePair[], itemIndex: number) => [] as Link[]}
|
||||
linksGetter={useCallback((span: Span, items: KeyValuePair[], itemIndex: number) => [] as Link[], [])}
|
||||
uiFind={search}
|
||||
/>
|
||||
</UIElementsContext.Provider>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { Span } from '@jaegertracing/jaeger-ui-components';
|
||||
|
||||
/**
|
||||
@@ -7,76 +7,88 @@ import { Span } from '@jaegertracing/jaeger-ui-components';
|
||||
export function useChildrenState() {
|
||||
const [childrenHiddenIDs, setChildrenHiddenIDs] = useState(new Set<string>());
|
||||
|
||||
function expandOne(spans: Span[]) {
|
||||
if (childrenHiddenIDs.size === 0) {
|
||||
return;
|
||||
}
|
||||
let prevExpandedDepth = -1;
|
||||
let expandNextHiddenSpan = true;
|
||||
const newChildrenHiddenIDs = spans.reduce((res, s) => {
|
||||
if (s.depth <= prevExpandedDepth) {
|
||||
expandNextHiddenSpan = true;
|
||||
const expandOne = useCallback(
|
||||
function expandOne(spans: Span[]) {
|
||||
if (childrenHiddenIDs.size === 0) {
|
||||
return;
|
||||
}
|
||||
if (expandNextHiddenSpan && res.has(s.spanID)) {
|
||||
res.delete(s.spanID);
|
||||
expandNextHiddenSpan = false;
|
||||
prevExpandedDepth = s.depth;
|
||||
}
|
||||
return res;
|
||||
}, new Set(childrenHiddenIDs));
|
||||
setChildrenHiddenIDs(newChildrenHiddenIDs);
|
||||
}
|
||||
let prevExpandedDepth = -1;
|
||||
let expandNextHiddenSpan = true;
|
||||
const newChildrenHiddenIDs = spans.reduce((res, s) => {
|
||||
if (s.depth <= prevExpandedDepth) {
|
||||
expandNextHiddenSpan = true;
|
||||
}
|
||||
if (expandNextHiddenSpan && res.has(s.spanID)) {
|
||||
res.delete(s.spanID);
|
||||
expandNextHiddenSpan = false;
|
||||
prevExpandedDepth = s.depth;
|
||||
}
|
||||
return res;
|
||||
}, new Set(childrenHiddenIDs));
|
||||
setChildrenHiddenIDs(newChildrenHiddenIDs);
|
||||
},
|
||||
[childrenHiddenIDs]
|
||||
);
|
||||
|
||||
function collapseOne(spans: Span[]) {
|
||||
if (shouldDisableCollapse(spans, childrenHiddenIDs)) {
|
||||
return;
|
||||
}
|
||||
let nearestCollapsedAncestor: Span | undefined;
|
||||
const newChildrenHiddenIDs = spans.reduce((res, curSpan) => {
|
||||
if (nearestCollapsedAncestor && curSpan.depth <= nearestCollapsedAncestor.depth) {
|
||||
res.add(nearestCollapsedAncestor.spanID);
|
||||
if (curSpan.hasChildren) {
|
||||
const collapseOne = useCallback(
|
||||
function collapseOne(spans: Span[]) {
|
||||
if (shouldDisableCollapse(spans, childrenHiddenIDs)) {
|
||||
return;
|
||||
}
|
||||
let nearestCollapsedAncestor: Span | undefined;
|
||||
const newChildrenHiddenIDs = spans.reduce((res, curSpan) => {
|
||||
if (nearestCollapsedAncestor && curSpan.depth <= nearestCollapsedAncestor.depth) {
|
||||
res.add(nearestCollapsedAncestor.spanID);
|
||||
if (curSpan.hasChildren) {
|
||||
nearestCollapsedAncestor = curSpan;
|
||||
}
|
||||
} else if (curSpan.hasChildren && !res.has(curSpan.spanID)) {
|
||||
nearestCollapsedAncestor = curSpan;
|
||||
}
|
||||
} else if (curSpan.hasChildren && !res.has(curSpan.spanID)) {
|
||||
nearestCollapsedAncestor = curSpan;
|
||||
return res;
|
||||
}, new Set(childrenHiddenIDs));
|
||||
// The last one
|
||||
if (nearestCollapsedAncestor) {
|
||||
newChildrenHiddenIDs.add(nearestCollapsedAncestor.spanID);
|
||||
}
|
||||
return res;
|
||||
}, new Set(childrenHiddenIDs));
|
||||
// The last one
|
||||
if (nearestCollapsedAncestor) {
|
||||
newChildrenHiddenIDs.add(nearestCollapsedAncestor.spanID);
|
||||
}
|
||||
setChildrenHiddenIDs(newChildrenHiddenIDs);
|
||||
}
|
||||
setChildrenHiddenIDs(newChildrenHiddenIDs);
|
||||
},
|
||||
[childrenHiddenIDs]
|
||||
);
|
||||
|
||||
function expandAll() {
|
||||
const expandAll = useCallback(function expandAll() {
|
||||
setChildrenHiddenIDs(new Set<string>());
|
||||
}
|
||||
}, []);
|
||||
|
||||
function collapseAll(spans: Span[]) {
|
||||
if (shouldDisableCollapse(spans, childrenHiddenIDs)) {
|
||||
return;
|
||||
}
|
||||
const newChildrenHiddenIDs = spans.reduce((res, s) => {
|
||||
if (s.hasChildren) {
|
||||
res.add(s.spanID);
|
||||
const collapseAll = useCallback(
|
||||
function collapseAll(spans: Span[]) {
|
||||
if (shouldDisableCollapse(spans, childrenHiddenIDs)) {
|
||||
return;
|
||||
}
|
||||
return res;
|
||||
}, new Set<string>());
|
||||
const newChildrenHiddenIDs = spans.reduce((res, s) => {
|
||||
if (s.hasChildren) {
|
||||
res.add(s.spanID);
|
||||
}
|
||||
return res;
|
||||
}, new Set<string>());
|
||||
|
||||
setChildrenHiddenIDs(newChildrenHiddenIDs);
|
||||
}
|
||||
setChildrenHiddenIDs(newChildrenHiddenIDs);
|
||||
},
|
||||
[childrenHiddenIDs]
|
||||
);
|
||||
|
||||
function childrenToggle(spanID: string) {
|
||||
const newChildrenHiddenIDs = new Set(childrenHiddenIDs);
|
||||
if (childrenHiddenIDs.has(spanID)) {
|
||||
newChildrenHiddenIDs.delete(spanID);
|
||||
} else {
|
||||
newChildrenHiddenIDs.add(spanID);
|
||||
}
|
||||
setChildrenHiddenIDs(newChildrenHiddenIDs);
|
||||
}
|
||||
const childrenToggle = useCallback(
|
||||
function childrenToggle(spanID: string) {
|
||||
const newChildrenHiddenIDs = new Set(childrenHiddenIDs);
|
||||
if (childrenHiddenIDs.has(spanID)) {
|
||||
newChildrenHiddenIDs.delete(spanID);
|
||||
} else {
|
||||
newChildrenHiddenIDs.add(spanID);
|
||||
}
|
||||
setChildrenHiddenIDs(newChildrenHiddenIDs);
|
||||
},
|
||||
[childrenHiddenIDs]
|
||||
);
|
||||
|
||||
return {
|
||||
childrenHiddenIDs,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { DetailState, Log } from '@jaegertracing/jaeger-ui-components';
|
||||
|
||||
/**
|
||||
@@ -8,36 +8,48 @@ import { DetailState, Log } from '@jaegertracing/jaeger-ui-components';
|
||||
export function useDetailState() {
|
||||
const [detailStates, setDetailStates] = useState(new Map<string, DetailState>());
|
||||
|
||||
function toggleDetail(spanID: string) {
|
||||
const newDetailStates = new Map(detailStates);
|
||||
if (newDetailStates.has(spanID)) {
|
||||
newDetailStates.delete(spanID);
|
||||
} else {
|
||||
newDetailStates.set(spanID, new DetailState());
|
||||
}
|
||||
setDetailStates(newDetailStates);
|
||||
}
|
||||
const toggleDetail = useCallback(
|
||||
function toggleDetail(spanID: string) {
|
||||
const newDetailStates = new Map(detailStates);
|
||||
if (newDetailStates.has(spanID)) {
|
||||
newDetailStates.delete(spanID);
|
||||
} else {
|
||||
newDetailStates.set(spanID, new DetailState());
|
||||
}
|
||||
setDetailStates(newDetailStates);
|
||||
},
|
||||
[detailStates]
|
||||
);
|
||||
|
||||
function detailLogItemToggle(spanID: string, log: Log) {
|
||||
const old = detailStates.get(spanID);
|
||||
if (!old) {
|
||||
return;
|
||||
}
|
||||
const detailState = old.toggleLogItem(log);
|
||||
const newDetailStates = new Map(detailStates);
|
||||
newDetailStates.set(spanID, detailState);
|
||||
return setDetailStates(newDetailStates);
|
||||
}
|
||||
const detailLogItemToggle = useCallback(
|
||||
function detailLogItemToggle(spanID: string, log: Log) {
|
||||
const old = detailStates.get(spanID);
|
||||
if (!old) {
|
||||
return;
|
||||
}
|
||||
const detailState = old.toggleLogItem(log);
|
||||
const newDetailStates = new Map(detailStates);
|
||||
newDetailStates.set(spanID, detailState);
|
||||
return setDetailStates(newDetailStates);
|
||||
},
|
||||
[detailStates]
|
||||
);
|
||||
|
||||
return {
|
||||
detailStates,
|
||||
toggleDetail,
|
||||
detailLogItemToggle,
|
||||
detailLogsToggle: makeDetailSubsectionToggle('logs', detailStates, setDetailStates),
|
||||
detailWarningsToggle: makeDetailSubsectionToggle('warnings', detailStates, setDetailStates),
|
||||
detailReferencesToggle: makeDetailSubsectionToggle('references', detailStates, setDetailStates),
|
||||
detailProcessToggle: makeDetailSubsectionToggle('process', detailStates, setDetailStates),
|
||||
detailTagsToggle: makeDetailSubsectionToggle('tags', detailStates, setDetailStates),
|
||||
detailLogsToggle: useCallback(makeDetailSubsectionToggle('logs', detailStates, setDetailStates), [detailStates]),
|
||||
detailWarningsToggle: useCallback(makeDetailSubsectionToggle('warnings', detailStates, setDetailStates), [
|
||||
detailStates,
|
||||
]),
|
||||
detailReferencesToggle: useCallback(makeDetailSubsectionToggle('references', detailStates, setDetailStates), [
|
||||
detailStates,
|
||||
]),
|
||||
detailProcessToggle: useCallback(makeDetailSubsectionToggle('process', detailStates, setDetailStates), [
|
||||
detailStates,
|
||||
]),
|
||||
detailTagsToggle: useCallback(makeDetailSubsectionToggle('tags', detailStates, setDetailStates), [detailStates]),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
/**
|
||||
* This is used internally to handle hover state of indent guide. As indent guides are separate
|
||||
@@ -10,21 +10,21 @@ import { useState } from 'react';
|
||||
export function useHoverIndentGuide() {
|
||||
const [hoverIndentGuideIds, setHoverIndentGuideIds] = useState(new Set<string>());
|
||||
|
||||
function addHoverIndentGuideId(spanID: string) {
|
||||
const addHoverIndentGuideId = useCallback(function addHoverIndentGuideId(spanID: string) {
|
||||
setHoverIndentGuideIds(prevState => {
|
||||
const newHoverIndentGuideIds = new Set(prevState);
|
||||
newHoverIndentGuideIds.add(spanID);
|
||||
return newHoverIndentGuideIds;
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
function removeHoverIndentGuideId(spanID: string) {
|
||||
const removeHoverIndentGuideId = useCallback(function removeHoverIndentGuideId(spanID: string) {
|
||||
setHoverIndentGuideIds(prevState => {
|
||||
const newHoverIndentGuideIds = new Set(prevState);
|
||||
newHoverIndentGuideIds.delete(spanID);
|
||||
return newHoverIndentGuideIds;
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { hoverIndentGuideIds, addHoverIndentGuideId, removeHoverIndentGuideId };
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Span, filterSpans } from '@jaegertracing/jaeger-ui-components';
|
||||
|
||||
/**
|
||||
@@ -7,9 +7,9 @@ import { Span, filterSpans } from '@jaegertracing/jaeger-ui-components';
|
||||
*/
|
||||
export function useSearch(spans?: Span[]) {
|
||||
const [search, setSearch] = useState('');
|
||||
let spanFindMatches: Set<string> | undefined;
|
||||
if (search && spans) {
|
||||
spanFindMatches = filterSpans(search, spans);
|
||||
}
|
||||
const spanFindMatches: Set<string> | undefined = useMemo(() => {
|
||||
return search && spans ? filterSpans(search, spans) : undefined;
|
||||
}, [search, spans]);
|
||||
|
||||
return { search, setSearch, spanFindMatches };
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { ViewRangeTimeUpdate, ViewRange } from '@jaegertracing/jaeger-ui-components';
|
||||
|
||||
/**
|
||||
@@ -12,16 +12,16 @@ export function useViewRange() {
|
||||
},
|
||||
});
|
||||
|
||||
function updateNextViewRangeTime(update: ViewRangeTimeUpdate) {
|
||||
const updateNextViewRangeTime = useCallback(function updateNextViewRangeTime(update: ViewRangeTimeUpdate) {
|
||||
setViewRange(
|
||||
(prevRange): ViewRange => {
|
||||
const time = { ...prevRange.time, ...update };
|
||||
return { ...prevRange, time };
|
||||
}
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
function updateViewRangeTime(start: number, end: number) {
|
||||
const updateViewRangeTime = useCallback(function updateViewRangeTime(start: number, end: number) {
|
||||
const current: [number, number] = [start, end];
|
||||
const time = { current };
|
||||
setViewRange(
|
||||
@@ -29,7 +29,7 @@ export function useViewRange() {
|
||||
return { ...prevRange, time };
|
||||
}
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { viewRange, updateViewRangeTime, updateNextViewRangeTime };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user