Skip to content

Commit 8326a1e

Browse files
committed
fixup: move throttling props to generic hook in Compass components
1 parent 379eed5 commit 8326a1e

File tree

3 files changed

+82
-91
lines changed

3 files changed

+82
-91
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { useEffect, useRef, useState } from 'react';
2+
3+
const DEFAULT_REFRESH_RATE_MS = 250;
4+
5+
export const useThrottledProps = <T extends Record<string, unknown>>(
6+
props: T,
7+
refreshRate: number = DEFAULT_REFRESH_RATE_MS
8+
): T => {
9+
// Throttling mechanism for Component content updates.
10+
const lastUpdateMS = useRef(0);
11+
const pendingUpdate = useRef<NodeJS.Timeout | null>(null);
12+
const [throttledProps, setThrottledProps] = useState<T>(props);
13+
14+
// Throttle props updating. This ensures we don't run
15+
// into broken state bugs with ReactFlow like COMPASS-9738.
16+
useEffect(() => {
17+
const now = Date.now();
18+
const timeSinceLastUpdate = now - lastUpdateMS.current;
19+
20+
const updateProps = () => {
21+
lastUpdateMS.current = Date.now();
22+
setThrottledProps(props);
23+
};
24+
25+
if (timeSinceLastUpdate >= refreshRate) {
26+
updateProps();
27+
} else {
28+
if (pendingUpdate.current) {
29+
clearTimeout(pendingUpdate.current);
30+
}
31+
32+
// Schedule update for the remaining time.
33+
const remainingTime = refreshRate - timeSinceLastUpdate;
34+
pendingUpdate.current = setTimeout(updateProps, remainingTime);
35+
}
36+
37+
return () => {
38+
if (pendingUpdate.current) {
39+
clearTimeout(pendingUpdate.current);
40+
pendingUpdate.current = null;
41+
}
42+
};
43+
}, [props, refreshRate]);
44+
45+
return throttledProps;
46+
};

packages/compass-components/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export {
152152
Theme,
153153
ThemeProvider,
154154
} from './hooks/use-theme';
155+
export { useThrottledProps } from './hooks/use-throttled-props';
155156
export {
156157
ContentWithFallback,
157158
FadeInPlaceholder,

packages/compass-data-modeling/src/components/diagram-editor.tsx

Lines changed: 35 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
useDarkMode,
3030
useDrawerActions,
3131
useDrawerState,
32+
useThrottledProps,
3233
rafraf,
3334
} from '@mongodb-js/compass-components';
3435
import { cancelAnalysis, retryAnalysis } from '../store/analysis-process';
@@ -121,8 +122,6 @@ const ZOOM_OPTIONS = {
121122
minZoom: 0.25,
122123
};
123124

124-
const REFRESH_DIAGRAM_RATE_MS = 250;
125-
126125
type SelectedItems = NonNullable<DiagramState>['selectedItems'];
127126

128127
const DiagramContent: React.FunctionComponent<{
@@ -321,95 +320,34 @@ const DiagramContent: React.FunctionComponent<{
321320
[handleNodesConnect]
322321
);
323322

324-
// Throttling mechanism for diagram content updates
325-
const lastDiagramUpdateMS = useRef(0);
326-
const pendingUpdate = useRef<NodeJS.Timeout | null>(null);
327-
const [throttledDiagramProps, setThrottledDiagramProps] = useState({
328-
isDarkMode,
329-
diagramLabel,
330-
edges,
331-
nodes,
332-
onNodeClick,
333-
onPaneClick,
334-
onEdgeClick,
335-
onFieldClick,
336-
onNodeDragStop,
337-
onConnect,
338-
});
339-
340-
// Throttle diagram props updating. This ensures we don't run
341-
// into broken state bugs with ReactFlow like COMPASS-9738.
342-
useEffect(() => {
343-
const now = Date.now();
344-
const timeSinceLastUpdate = now - lastDiagramUpdateMS.current;
345-
346-
const updateProps = () => {
347-
lastDiagramUpdateMS.current = Date.now();
348-
setThrottledDiagramProps({
349-
isDarkMode,
350-
diagramLabel,
351-
edges,
352-
nodes,
353-
onNodeClick,
354-
onPaneClick,
355-
onEdgeClick,
356-
onFieldClick,
357-
onNodeDragStop,
358-
onConnect,
359-
});
360-
};
361-
362-
if (timeSinceLastUpdate >= REFRESH_DIAGRAM_RATE_MS) {
363-
updateProps();
364-
} else {
365-
if (pendingUpdate.current) {
366-
clearTimeout(pendingUpdate.current);
367-
}
368-
369-
// Schedule update for the remaining time.
370-
const remainingTime = REFRESH_DIAGRAM_RATE_MS - timeSinceLastUpdate;
371-
pendingUpdate.current = setTimeout(updateProps, remainingTime);
372-
}
373-
374-
return () => {
375-
if (pendingUpdate.current) {
376-
clearTimeout(pendingUpdate.current);
377-
pendingUpdate.current = null;
378-
}
379-
};
380-
}, [
381-
isDarkMode,
382-
diagramLabel,
383-
edges,
384-
nodes,
385-
onNodeClick,
386-
onPaneClick,
387-
onEdgeClick,
388-
onFieldClick,
389-
onNodeDragStop,
390-
onConnect,
391-
]);
323+
const diagramProps = useMemo(
324+
() => ({
325+
isDarkMode,
326+
title: diagramLabel,
327+
edges,
328+
nodes,
329+
onNodeClick,
330+
onPaneClick,
331+
onEdgeClick,
332+
onFieldClick,
333+
onNodeDragStop,
334+
onConnect,
335+
}),
336+
[
337+
isDarkMode,
338+
diagramLabel,
339+
edges,
340+
nodes,
341+
onNodeClick,
342+
onPaneClick,
343+
onEdgeClick,
344+
onFieldClick,
345+
onNodeDragStop,
346+
onConnect,
347+
]
348+
);
392349

393-
const diagramContent = useMemo(() => {
394-
return (
395-
<Diagram
396-
isDarkMode={throttledDiagramProps.isDarkMode}
397-
title={throttledDiagramProps.diagramLabel}
398-
edges={throttledDiagramProps.edges}
399-
nodes={throttledDiagramProps.nodes}
400-
// With threshold too low clicking sometimes gets confused with
401-
// dragging
402-
nodeDragThreshold={5}
403-
onNodeClick={throttledDiagramProps.onNodeClick}
404-
onPaneClick={throttledDiagramProps.onPaneClick}
405-
onEdgeClick={throttledDiagramProps.onEdgeClick}
406-
onFieldClick={throttledDiagramProps.onFieldClick}
407-
fitViewOptions={ZOOM_OPTIONS}
408-
onNodeDragStop={throttledDiagramProps.onNodeDragStop}
409-
onConnect={throttledDiagramProps.onConnect}
410-
/>
411-
);
412-
}, [throttledDiagramProps]);
350+
const throttledDiagramProps = useThrottledProps(diagramProps);
413351

414352
return (
415353
<div
@@ -432,7 +370,13 @@ const DiagramContent: React.FunctionComponent<{
432370
impact your data
433371
</Banner>
434372
)}
435-
{diagramContent}
373+
<Diagram
374+
{...throttledDiagramProps}
375+
// With threshold too low clicking sometimes gets confused with
376+
// dragging.
377+
nodeDragThreshold={5}
378+
fitViewOptions={ZOOM_OPTIONS}
379+
/>
436380
</div>
437381
</div>
438382
);

0 commit comments

Comments
 (0)