From 47661c31f1834279049cc58bc988bab207ca5d3e Mon Sep 17 00:00:00 2001 From: aiyimickey <39365912+aiyimickey@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:41:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20useDirtyTracker=20?= =?UTF-8?q?Hook=20=E2=80=94=20=E8=84=8F=E7=8A=B6=E6=80=81=E8=BF=BD?= =?UTF-8?q?=E8=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持数组/表单'修改后才显示保存按钮'模式: - isDirty(key, currentItem): 判断指定key是否相对上次保存有变更 - markClean(key, item): 标记为已保存,隐藏保存按钮 - removeItem(key): 清理已删除项的追踪记录 --- ui/hooks/index.ts | 1 + ui/hooks/useDirtyTracker.ts | 46 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 ui/hooks/index.ts create mode 100644 ui/hooks/useDirtyTracker.ts diff --git a/ui/hooks/index.ts b/ui/hooks/index.ts new file mode 100644 index 0000000..9c1f1aa --- /dev/null +++ b/ui/hooks/index.ts @@ -0,0 +1 @@ +export { useDirtyTracker } from './useDirtyTracker' diff --git a/ui/hooks/useDirtyTracker.ts b/ui/hooks/useDirtyTracker.ts new file mode 100644 index 0000000..e2621e5 --- /dev/null +++ b/ui/hooks/useDirtyTracker.ts @@ -0,0 +1,46 @@ +'use client' +import { useState, useRef } from 'react' + +/** + * 脏状态追踪 Hook — 用于"修改后才显示保存按钮"模式。 + * + * @example 数组场景(每个子项独立保存) + * const { isDirty, markClean, removeItem } = useDirtyTracker() + * {items.map(item => ( + *
+ * + * {isDirty(item.id, item) && } + *
+ * ))} + * // 保存成功后: markClean(id, savedItem) + * // 删除时: removeItem(id) + */ +export function useDirtyTracker() { + const savedMap = useRef>(new Map()) + + /** + * 判断指定 key 的当前数据是否相对于上次 markClean 有修改。 + * 从未 markClean 过的 key 视为 dirty(适用于新增项)。 + */ + const isDirty = (key: string, current: unknown): boolean => { + const saved = savedMap.current.get(key) + if (saved === undefined) return true // 从未保存过 → dirty + return saved !== JSON.stringify(current) + } + + /** + * 将指定 key 的当前数据标记为"已保存"(重置 dirty 状态)。 + */ + const markClean = (key: string, item: unknown): void => { + savedMap.current.set(key, JSON.stringify(item)) + } + + /** + * 删除指定 key 的追踪记录(用于列表项被删除时清理)。 + */ + const removeItem = (key: string): void => { + savedMap.current.delete(key) + } + + return { isDirty, markClean, removeItem } +}