This commit is contained in:
@@ -67,6 +67,14 @@ export const useUpdateCatalog = () => {
|
|||||||
mutationFn: api.updateCatalog,
|
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<ReturnType<typeof setTimeout> | null>(null);
|
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
const pendingParamsRef = useRef<
|
const pendingParamsRef = useRef<
|
||||||
Parameters<typeof api.updateCatalog>[0] | null
|
Parameters<typeof api.updateCatalog>[0] | null
|
||||||
@@ -96,11 +104,11 @@ export const useUpdateCatalog = () => {
|
|||||||
pendingParamsRef.current = null;
|
pendingParamsRef.current = null;
|
||||||
setIsScheduledSaving(false);
|
setIsScheduledSaving(false);
|
||||||
if (pendingParams) {
|
if (pendingParams) {
|
||||||
mutation.mutate(pendingParams);
|
mutationRef.current.mutate(pendingParams);
|
||||||
}
|
}
|
||||||
}, delayMs);
|
}, delayMs);
|
||||||
},
|
},
|
||||||
[mutation],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
@@ -112,12 +120,12 @@ export const useUpdateCatalog = () => {
|
|||||||
// اگر کاربر قبل از پایان debounce از ادیتور خارج شد،
|
// اگر کاربر قبل از پایان debounce از ادیتور خارج شد،
|
||||||
// آخرین تغییرات معلق را همان لحظه ذخیره کن.
|
// آخرین تغییرات معلق را همان لحظه ذخیره کن.
|
||||||
if (pendingParamsRef.current) {
|
if (pendingParamsRef.current) {
|
||||||
mutation.mutate(pendingParamsRef.current);
|
mutationRef.current.mutate(pendingParamsRef.current);
|
||||||
pendingParamsRef.current = null;
|
pendingParamsRef.current = null;
|
||||||
}
|
}
|
||||||
setIsScheduledSaving(false);
|
setIsScheduledSaving(false);
|
||||||
},
|
},
|
||||||
[mutation],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -64,11 +64,7 @@ const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onDragMove={(e) => {
|
onDragMove={(e) => {
|
||||||
const node = e.target;
|
e.target.getLayer()?.batchDraw();
|
||||||
onUpdate(obj.id, {
|
|
||||||
x: node.x() - (obj.width || 100) / 2,
|
|
||||||
y: node.y() - (obj.height || 100) / 2,
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
onDragEnd={(e) => {
|
onDragEnd={(e) => {
|
||||||
const node = e.target;
|
const node = e.target;
|
||||||
|
|||||||
@@ -53,11 +53,7 @@ const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapePr
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onDragMove={(e) => {
|
onDragMove={(e) => {
|
||||||
const node = e.target;
|
e.target.getLayer()?.batchDraw();
|
||||||
onUpdate(obj.id, {
|
|
||||||
x: node.x(),
|
|
||||||
y: node.y(),
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
onDragEnd={(e) => {
|
onDragEnd={(e) => {
|
||||||
const node = e.target;
|
const node = e.target;
|
||||||
|
|||||||
@@ -60,11 +60,7 @@ const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: Shape
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onDragMove={(e) => {
|
onDragMove={(e) => {
|
||||||
const node = e.target;
|
e.target.getLayer()?.batchDraw();
|
||||||
onUpdate(obj.id, {
|
|
||||||
x: node.x() - (obj.width || 100) / 2,
|
|
||||||
y: node.y() - (obj.height || 100) / 2,
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
onDragEnd={(e) => {
|
onDragEnd={(e) => {
|
||||||
const node = e.target;
|
const node = e.target;
|
||||||
|
|||||||
Reference in New Issue
Block a user