BLUE
PHP 7.4.33
Path:
/var/www/snow-ecom.wpress.dk/httpdocs/src/hooks
Run
Logout
Edit File
Size: 2.97 KB
Close
/var/www/snow-ecom.wpress.dk/httpdocs/src/hooks/useDebouncedMultiRowSave.ts
Text
Base64
"use client"; import { useCallback, useEffect, useRef } from "react"; type BatchCallbacks = { onBatchStart?: () => void; onBatchComplete?: (result: { attempted: number; failed: number }) => void; }; /** * Reliable autosave for editable tables: tracks many dirty row ids, debounces once the user pauses, * then saves each dirty row using the latest {@link allItems} snapshot (per-row errors do not block others). */ export function useDebouncedMultiRowSave<T extends { ItemId: number }>( allItems: T[], debounceMs: number, saveRow: (item: T) => Promise<void>, callbacks?: BatchCallbacks ) { const allItemsRef = useRef(allItems); useEffect(() => { allItemsRef.current = allItems; }, [allItems]); const saveRowRef = useRef(saveRow); useEffect(() => { saveRowRef.current = saveRow; }, [saveRow]); const callbacksRef = useRef(callbacks); useEffect(() => { callbacksRef.current = callbacks; }, [callbacks]); const dirtyIdsRef = useRef<Set<number>>(new Set()); const debounceTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null); const isFlushingRef = useRef(false); const runFlushCycle = useCallback(async () => { if (isFlushingRef.current) { return; } const ids = Array.from(dirtyIdsRef.current); if (ids.length === 0) { return; } dirtyIdsRef.current.clear(); isFlushingRef.current = true; callbacksRef.current?.onBatchStart?.(); const byId = new Map(allItemsRef.current.map((i) => [i.ItemId, i])); const failed: number[] = []; for (const id of ids) { const row = byId.get(id); if (!row) { continue; } try { await saveRowRef.current(row); } catch (err) { console.error(err); failed.push(id); } } failed.forEach((id) => dirtyIdsRef.current.add(id)); isFlushingRef.current = false; callbacksRef.current?.onBatchComplete?.({ attempted: ids.length, failed: failed.length, }); if (dirtyIdsRef.current.size > 0) { if (debounceTimeoutRef.current) { clearTimeout(debounceTimeoutRef.current); } debounceTimeoutRef.current = setTimeout(() => { debounceTimeoutRef.current = null; void runFlushCycle(); }, debounceMs); } }, [debounceMs]); const scheduleFlush = useCallback(() => { if (debounceTimeoutRef.current) { clearTimeout(debounceTimeoutRef.current); } debounceTimeoutRef.current = setTimeout(() => { debounceTimeoutRef.current = null; void runFlushCycle(); }, debounceMs); }, [debounceMs, runFlushCycle]); const markDirty = useCallback( (itemId: number) => { dirtyIdsRef.current.add(itemId); scheduleFlush(); }, [scheduleFlush] ); useEffect( () => () => { if (debounceTimeoutRef.current) { clearTimeout(debounceTimeoutRef.current); debounceTimeoutRef.current = null; } }, [] ); return { markDirty, scheduleFlush, flushNow: runFlushCycle }; }
Save
Close
Exit & Reset
Text mode: syntax highlighting auto-detects file type.
Directory Contents
Dirs: 0 × Files: 3
Delete Selected
Select All
Select None
Sort:
Name
Size
Modified
Enable drag-to-move
Name
Size
Perms
Modified
Actions
use-mobile.tsx
565 B
lrw-r--r--
2026-05-13 05:33:57
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
useDebouncedMultiRowSave.ts
2.97 KB
lrw-r--r--
2026-05-13 05:33:57
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
useLogging.ts
2.75 KB
lrw-r--r--
2026-05-13 05:33:57
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
Zip Selected
If ZipArchive is unavailable, a
.tar
will be created (no compression).