优化:修改撤回

This commit is contained in:
iqudoo
2026-06-12 23:53:19 +08:00
parent 5b4f9de364
commit 4167fb2683
4 changed files with 177 additions and 10 deletions

65
src/hooks/useUndoRedo.ts Normal file
View File

@@ -0,0 +1,65 @@
import { useCallback, useRef, useState } from 'react';
const MAX_HISTORY = 100;
function cloneSnapshot<T>(value: T): T {
return JSON.parse(JSON.stringify(value)) as T;
}
export function useUndoRedo<T>() {
const pastRef = useRef<T[]>([]);
const futureRef = useRef<T[]>([]);
const [revision, setRevision] = useState(0);
const bump = useCallback(() => setRevision((n) => n + 1), []);
const reset = useCallback(() => {
pastRef.current = [];
futureRef.current = [];
bump();
}, [bump]);
const record = useCallback(
(previous: T, next: T) => {
if (JSON.stringify(previous) === JSON.stringify(next)) return;
pastRef.current = [...pastRef.current, cloneSnapshot(previous)].slice(-MAX_HISTORY);
futureRef.current = [];
bump();
},
[bump]
);
const undo = useCallback(
(current: T): T | null => {
if (pastRef.current.length === 0) return null;
const previous = pastRef.current[pastRef.current.length - 1];
pastRef.current = pastRef.current.slice(0, -1);
futureRef.current = [cloneSnapshot(current), ...futureRef.current];
bump();
return cloneSnapshot(previous);
},
[bump]
);
const redo = useCallback(
(current: T): T | null => {
if (futureRef.current.length === 0) return null;
const next = futureRef.current[0];
futureRef.current = futureRef.current.slice(1);
pastRef.current = [...pastRef.current, cloneSnapshot(current)];
bump();
return cloneSnapshot(next);
},
[bump]
);
return {
revision,
canUndo: pastRef.current.length > 0,
canRedo: futureRef.current.length > 0,
reset,
record,
undo,
redo,
};
}