From b13a46ec65a5786e1f813ac23904c3620af6c830 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sat, 20 Jun 2026 12:59:39 +0330 Subject: [PATCH] auto save fix --- src/pages/catalogue/hooks/useCatalogueData.ts | 16 ++++++++++++---- .../editor/components/tools/AbstractShape.tsx | 6 +----- .../editor/components/tools/CircleShape.tsx | 6 +----- .../editor/components/tools/TriangleShape.tsx | 6 +----- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/pages/catalogue/hooks/useCatalogueData.ts b/src/pages/catalogue/hooks/useCatalogueData.ts index 62fa202..35567f2 100644 --- a/src/pages/catalogue/hooks/useCatalogueData.ts +++ b/src/pages/catalogue/hooks/useCatalogueData.ts @@ -67,6 +67,14 @@ export const useUpdateCatalog = () => { mutationFn: api.updateCatalog, }); + // Ref to always hold the latest mutation without adding it to useCallback/useEffect deps. + // React Query's useMutation returns a new object reference on every render, so + // closing over `mutation` directly would cause scheduleUpdate and the cleanup effect + // to be recreated/re-run on every render — firing mutation.mutate on each re-render + // during drag (potentially 100+ times). + const mutationRef = useRef(mutation); + mutationRef.current = mutation; + const timeoutRef = useRef | null>(null); const pendingParamsRef = useRef< Parameters[0] | null @@ -96,11 +104,11 @@ export const useUpdateCatalog = () => { pendingParamsRef.current = null; setIsScheduledSaving(false); if (pendingParams) { - mutation.mutate(pendingParams); + mutationRef.current.mutate(pendingParams); } }, delayMs); }, - [mutation], + [], ); useEffect( @@ -112,12 +120,12 @@ export const useUpdateCatalog = () => { // اگر کاربر قبل از پایان debounce از ادیتور خارج شد، // آخرین تغییرات معلق را همان لحظه ذخیره کن. if (pendingParamsRef.current) { - mutation.mutate(pendingParamsRef.current); + mutationRef.current.mutate(pendingParamsRef.current); pendingParamsRef.current = null; } setIsScheduledSaving(false); }, - [mutation], + [], ); return { diff --git a/src/pages/editor/components/tools/AbstractShape.tsx b/src/pages/editor/components/tools/AbstractShape.tsx index b95f12d..b20f085 100644 --- a/src/pages/editor/components/tools/AbstractShape.tsx +++ b/src/pages/editor/components/tools/AbstractShape.tsx @@ -64,11 +64,7 @@ const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape } }} onDragMove={(e) => { - const node = e.target; - onUpdate(obj.id, { - x: node.x() - (obj.width || 100) / 2, - y: node.y() - (obj.height || 100) / 2, - }); + e.target.getLayer()?.batchDraw(); }} onDragEnd={(e) => { const node = e.target; diff --git a/src/pages/editor/components/tools/CircleShape.tsx b/src/pages/editor/components/tools/CircleShape.tsx index b0dc3b3..7a6a1cb 100644 --- a/src/pages/editor/components/tools/CircleShape.tsx +++ b/src/pages/editor/components/tools/CircleShape.tsx @@ -53,11 +53,7 @@ const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapePr } }} onDragMove={(e) => { - const node = e.target; - onUpdate(obj.id, { - x: node.x(), - y: node.y(), - }); + e.target.getLayer()?.batchDraw(); }} onDragEnd={(e) => { const node = e.target; diff --git a/src/pages/editor/components/tools/TriangleShape.tsx b/src/pages/editor/components/tools/TriangleShape.tsx index 776d95e..6986e2e 100644 --- a/src/pages/editor/components/tools/TriangleShape.tsx +++ b/src/pages/editor/components/tools/TriangleShape.tsx @@ -60,11 +60,7 @@ const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape } }} onDragMove={(e) => { - const node = e.target; - onUpdate(obj.id, { - x: node.x() - (obj.width || 100) / 2, - y: node.y() - (obj.height || 100) / 2, - }); + e.target.getLayer()?.batchDraw(); }} onDragEnd={(e) => { const node = e.target;