mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: cache useSyncExternalStore snapshot to prevent infinite re-render
getSnapshot() was calling JSON.parse() on every invocation, returning a new array reference each time. useSyncExternalStore uses Object.is comparison, so it saw a "new" value on every render and triggered an infinite update loop. Fix by caching the parsed result and only re-parsing when the raw localStorage value changes.
This commit is contained in:
@@ -2,17 +2,28 @@ import { useSyncExternalStore } from "react";
|
|||||||
|
|
||||||
const STORAGE_KEY = "snapotter-recent-tools";
|
const STORAGE_KEY = "snapotter-recent-tools";
|
||||||
const MAX_RECENT = 5;
|
const MAX_RECENT = 5;
|
||||||
|
const EMPTY: string[] = [];
|
||||||
|
|
||||||
let listeners: Array<() => void> = [];
|
let listeners: Array<() => void> = [];
|
||||||
|
let cachedRaw: string | null = null;
|
||||||
|
let cachedParsed: string[] = EMPTY;
|
||||||
|
|
||||||
function getSnapshot(): string[] {
|
function getSnapshot(): string[] {
|
||||||
try {
|
|
||||||
const raw = localStorage.getItem(STORAGE_KEY);
|
const raw = localStorage.getItem(STORAGE_KEY);
|
||||||
return raw ? JSON.parse(raw) : [];
|
if (raw !== cachedRaw) {
|
||||||
|
cachedRaw = raw;
|
||||||
|
try {
|
||||||
|
cachedParsed = raw ? JSON.parse(raw) : EMPTY;
|
||||||
} catch {
|
} catch {
|
||||||
return [];
|
cachedParsed = EMPTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return cachedParsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServerSnapshot(): string[] {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
function subscribe(listener: () => void) {
|
function subscribe(listener: () => void) {
|
||||||
listeners.push(listener);
|
listeners.push(listener);
|
||||||
@@ -25,9 +36,10 @@ export function recordRecentTool(toolId: string) {
|
|||||||
const current = getSnapshot();
|
const current = getSnapshot();
|
||||||
const updated = [toolId, ...current.filter((id) => id !== toolId)].slice(0, MAX_RECENT);
|
const updated = [toolId, ...current.filter((id) => id !== toolId)].slice(0, MAX_RECENT);
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
|
||||||
|
cachedRaw = null;
|
||||||
listeners.forEach((l) => l());
|
listeners.forEach((l) => l());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useRecentTools(): string[] {
|
export function useRecentTools(): string[] {
|
||||||
return useSyncExternalStore(subscribe, getSnapshot, () => []);
|
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user