mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
fix: extracting debounced update to a custom hook
This commit is contained in:
parent
24b8bcf1ba
commit
dbf1fb002f
@ -32,7 +32,13 @@ import {
|
|||||||
DiagramModel,
|
DiagramModel,
|
||||||
DiagramModelGenerics,
|
DiagramModelGenerics,
|
||||||
} from "@projectstorm/react-diagrams";
|
} from "@projectstorm/react-diagrams";
|
||||||
import { SyntheticEvent, useEffect, useRef, useState } from "react";
|
import {
|
||||||
|
SyntheticEvent,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { DeleteDialog } from "@/app-components/dialogs";
|
import { DeleteDialog } from "@/app-components/dialogs";
|
||||||
@ -45,13 +51,13 @@ import { getDisplayDialogs, useDialog } from "@/hooks/useDialog";
|
|||||||
import { useSearch } from "@/hooks/useSearch";
|
import { useSearch } from "@/hooks/useSearch";
|
||||||
import { EntityType, Format } from "@/services/types";
|
import { EntityType, Format } from "@/services/types";
|
||||||
import { IBlock } from "@/types/block.types";
|
import { IBlock } from "@/types/block.types";
|
||||||
import { ICategory } from "@/types/category.types";
|
import { ICategory, ICategoryAttributes } from "@/types/category.types";
|
||||||
import { BlockPorts } from "@/types/visual-editor.types";
|
import { BlockPorts } from "@/types/visual-editor.types";
|
||||||
|
|
||||||
import BlockDialog from "../BlockDialog";
|
import BlockDialog from "../BlockDialog";
|
||||||
import { ZOOM_LEVEL } from "../constants";
|
import { ZOOM_LEVEL } from "../constants";
|
||||||
import { useVisualEditor } from "../hooks/useVisualEditor";
|
import { useVisualEditor } from "../hooks/useVisualEditor";
|
||||||
import { RequestQueue } from "@/utils/requestQueue";
|
import useDebouncedUpdate from "@/hooks/useDebouncedUpdate";
|
||||||
|
|
||||||
const Diagrams = () => {
|
const Diagrams = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -110,26 +116,37 @@ const Diagrams = () => {
|
|||||||
invalidate: false,
|
invalidate: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const requestQueue = useRef(new RequestQueue<ICategory>());
|
const debouncedUpdateCategory = useDebouncedUpdate(updateCategory, 300);
|
||||||
const enqueueUpdate = (id: string, params: any) => {
|
const debouncedZoomEvent = useCallback(
|
||||||
requestQueue.current.enqueue(() => updateCategory({ id, params }));
|
(event: any) => {
|
||||||
};
|
if (selectedCategoryId) {
|
||||||
|
engine?.repaintCanvas();
|
||||||
|
debouncedUpdateCategory({
|
||||||
|
id: selectedCategoryId,
|
||||||
|
params: {
|
||||||
|
zoom: event.zoom,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
event.stopPropagation();
|
||||||
|
},
|
||||||
|
[selectedCategoryId, debouncedUpdateCategory],
|
||||||
|
);
|
||||||
|
const debouncedOffsetEvent = useCallback(
|
||||||
|
(event: any) => {
|
||||||
|
if (selectedCategoryId) {
|
||||||
|
debouncedUpdateCategory({
|
||||||
|
id: selectedCategoryId,
|
||||||
|
params: {
|
||||||
|
offset: [event.offsetX, event.offsetY],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
event.stopPropagation();
|
||||||
|
},
|
||||||
|
[selectedCategoryId, debouncedUpdateCategory],
|
||||||
|
);
|
||||||
|
|
||||||
const debouncedZoomEvent = debounce((event) => {
|
|
||||||
if (selectedCategoryId) {
|
|
||||||
engine?.repaintCanvas();
|
|
||||||
enqueueUpdate(selectedCategoryId, { zoom: event.zoom });
|
|
||||||
}
|
|
||||||
event.stopPropagation();
|
|
||||||
}, 200);
|
|
||||||
const debouncedOffsetEvent = debounce((event) => {
|
|
||||||
if (selectedCategoryId) {
|
|
||||||
enqueueUpdate(selectedCategoryId, {
|
|
||||||
offset: [event.offsetX, event.offsetY],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
event.stopPropagation();
|
|
||||||
}, 200);
|
|
||||||
const getBlockFromCache = useGetFromCache(EntityType.BLOCK);
|
const getBlockFromCache = useGetFromCache(EntityType.BLOCK);
|
||||||
const updateCachedBlock = useUpdateCache(EntityType.BLOCK);
|
const updateCachedBlock = useUpdateCache(EntityType.BLOCK);
|
||||||
const deleteCachedBlock = useDeleteFromCache(EntityType.BLOCK);
|
const deleteCachedBlock = useDeleteFromCache(EntityType.BLOCK);
|
||||||
|
47
frontend/src/hooks/useDebouncedUpdate.tsx
Normal file
47
frontend/src/hooks/useDebouncedUpdate.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { debounce } from "@mui/material";
|
||||||
|
import { useCallback, useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
type DebouncedUpdateParams = {
|
||||||
|
id: string;
|
||||||
|
params: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function useDebouncedUpdate(
|
||||||
|
apiUpdate: (params: DebouncedUpdateParams) => void,
|
||||||
|
delay: number = 300,
|
||||||
|
) {
|
||||||
|
const accumulatedUpdates = useRef<DebouncedUpdateParams | null>(null);
|
||||||
|
|
||||||
|
const processUpdates = useRef(
|
||||||
|
debounce(() => {
|
||||||
|
if (accumulatedUpdates.current) {
|
||||||
|
apiUpdate(accumulatedUpdates.current);
|
||||||
|
accumulatedUpdates.current = null;
|
||||||
|
}
|
||||||
|
}, delay),
|
||||||
|
).current;
|
||||||
|
|
||||||
|
const handleUpdate = useCallback(
|
||||||
|
(params: DebouncedUpdateParams) => {
|
||||||
|
accumulatedUpdates.current = {
|
||||||
|
id: params.id,
|
||||||
|
params: {
|
||||||
|
...(accumulatedUpdates.current?.params || {}),
|
||||||
|
...params.params,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
processUpdates();
|
||||||
|
},
|
||||||
|
[processUpdates],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
processUpdates.clear();
|
||||||
|
};
|
||||||
|
}, [processUpdates]);
|
||||||
|
|
||||||
|
return handleUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useDebouncedUpdate;
|
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2024 Hexastack. All rights reserved.
|
|
||||||
*
|
|
||||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
|
||||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
|
||||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
|
||||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export class RequestQueue<T> {
|
|
||||||
private queue: Array<() => Promise<T>> = [];
|
|
||||||
private isProcessing = false;
|
|
||||||
|
|
||||||
enqueue(request: () => Promise<T>) {
|
|
||||||
this.queue.push(request);
|
|
||||||
this.processQueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async processQueue() {
|
|
||||||
if (this.isProcessing) return;
|
|
||||||
|
|
||||||
this.isProcessing = true;
|
|
||||||
|
|
||||||
while (this.queue.length > 0) {
|
|
||||||
const request = this.queue.shift();
|
|
||||||
if (request) {
|
|
||||||
await request();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isProcessing = false;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user