Ver Fonte

Fix React hydration error in useLocalStorage hook (#10)

**Problem:**
React error #418 (hydration mismatch) occurred on every page load because
useLocalStorage accessed localStorage during initial render, causing server
and client to render different content.

**Root Cause:**
The hook was reading from localStorage in useState initializer:
```typescript
const [storedValue, setStoredValue] = useState<T>(() => {
  const item = window.localStorage.getItem(key);
  return item ? JSON.parse(item) : initialValue;
});
```

This caused:
1. Server renders with `initialValue`
2. Client hydrates and reads different value from localStorage
3. React detects mismatch and throws error #418

**Solution:**
Implemented proper SSR-safe pattern:
1. Always use `initialValue` for initial state (same on server and client)
2. Load from localStorage in `useEffect` (client-side only, after hydration)
3. This ensures server and client render identical content initially

Changes in webui/lib/hooks.ts:
```typescript
// Always start with initialValue to prevent hydration mismatch
const [storedValue, setStoredValue] = useState<T>(initialValue);

// Load from localStorage after component mounts (client-side only)
useEffect(() => {
  try {
    const item = window.localStorage.getItem(key);
    if (item) {
      setStoredValue(JSON.parse(item));
    }
  } catch (error) {
    console.warn(`Error loading localStorage key "${key}":`, error);
  }
}, [key]);
```

**Result:**
- No more hydration errors
- Form data still persists correctly
- Smooth page loads without console errors
- Proper SSR/client-side rendering

**Impact:**
Fixes hydration errors on all pages using useLocalStorage:
- Text to Image page
- Image to Image page
- Upscaler page

Fixes #10
Fszontagh há 3 meses atrás
pai
commit
614f2b0ef2
1 ficheiros alterados com 10 adições e 12 exclusões
  1. 10 12
      webui/lib/hooks.ts

+ 10 - 12
webui/lib/hooks.ts

@@ -10,23 +10,21 @@ export function useLocalStorage<T>(
   key: string,
   initialValue: T
 ): [T, (value: T | ((prevValue: T) => T)) => void, () => void] {
-  // State to store our value
-  // Pass initial state function to useState so logic is only executed once
-  const [storedValue, setStoredValue] = useState<T>(() => {
-    if (typeof window === 'undefined') {
-      return initialValue;
-    }
+  // Always start with initialValue to prevent hydration mismatch
+  // Load from localStorage only after client-side hydration
+  const [storedValue, setStoredValue] = useState<T>(initialValue);
+
+  // Load value from localStorage after component mounts (client-side only)
+  useEffect(() => {
     try {
-      // Get from local storage by key
       const item = window.localStorage.getItem(key);
-      // Parse stored json or if none return initialValue
-      return item ? JSON.parse(item) : initialValue;
+      if (item) {
+        setStoredValue(JSON.parse(item));
+      }
     } catch (error) {
-      // If error also return initialValue
       console.warn(`Error loading localStorage key "${key}":`, error);
-      return initialValue;
     }
-  });
+  }, [key]);
 
   // Return a wrapped version of useState's setter function that ...
   // ... persists the new value to localStorage.