Vollständige Next.js 15 Portfolio-Website mit: - Blog-System mit 100+ Artikeln - Supabase-Integration - Responsive Design mit Tailwind CSS - TypeScript-Konfiguration - Testing-Setup mit Vitest und Playwright Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
100 lines
3.3 KiB
TypeScript
100 lines
3.3 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
import { handleError } from '../utils/errorHandling';
|
|
|
|
/**
|
|
* Function type for updating the stored value
|
|
* @template T - The type of the stored value
|
|
* @param value - Either a new value of type T or an updater function that takes the current value and returns a new value
|
|
*/
|
|
type SetValue<T> = (value: T | ((val: T) => T)) => void;
|
|
|
|
/**
|
|
* Custom hook for managing state in localStorage with automatic serialization
|
|
*
|
|
* @template T - The type of the stored value
|
|
* @param {string} key - The localStorage key to use
|
|
* @param {T | (() => T)} initialValue - The initial value or a function that returns the initial value
|
|
* @returns {[T, SetValue<T>, () => void]} A tuple containing:
|
|
* - The current stored value
|
|
* - A function to update the stored value
|
|
* - A function to remove the value from storage
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const [theme, setTheme, removeTheme] = useLocalStorage('theme', 'light');
|
|
* setTheme('dark'); // Updates both state and localStorage
|
|
* removeTheme(); // Removes from localStorage and resets to initial value
|
|
* ```
|
|
*
|
|
* @remarks
|
|
* - SSR-safe: Returns initial value during server-side rendering
|
|
* - Automatically serializes/deserializes JSON
|
|
* - Handles localStorage quota exceeded errors
|
|
* - Handles invalid JSON gracefully
|
|
*/
|
|
export function useLocalStorage<T>(
|
|
key: string,
|
|
initialValue: T | (() => T)
|
|
): [T, SetValue<T>, () => void] {
|
|
// Get initial value - SSR safe
|
|
const getInitialValue = useCallback((): T => {
|
|
// Check if we're in a browser environment
|
|
if (typeof window === 'undefined') {
|
|
return initialValue instanceof Function ? initialValue() : initialValue;
|
|
}
|
|
|
|
try {
|
|
const item = window.localStorage.getItem(key);
|
|
if (item) {
|
|
return JSON.parse(item) as T;
|
|
}
|
|
} catch (error) {
|
|
handleError(error, `LocalStorage Read (key: ${key})`);
|
|
}
|
|
|
|
return initialValue instanceof Function ? initialValue() : initialValue;
|
|
}, [key, initialValue]);
|
|
|
|
const [storedValue, setStoredValue] = useState<T>(getInitialValue);
|
|
|
|
// Update localStorage whenever storedValue changes
|
|
useEffect(() => {
|
|
if (typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
window.localStorage.setItem(key, JSON.stringify(storedValue));
|
|
} catch (error) {
|
|
handleError(error, `LocalStorage Write (key: ${key})`);
|
|
}
|
|
}, [key, storedValue]);
|
|
|
|
// Set value function that supports both direct values and updater functions
|
|
const setValue: SetValue<T> = useCallback((value) => {
|
|
try {
|
|
setStoredValue((prevValue) => {
|
|
const newValue = value instanceof Function ? value(prevValue) : value;
|
|
return newValue;
|
|
});
|
|
} catch (error) {
|
|
handleError(error, `LocalStorage Update (key: ${key})`);
|
|
}
|
|
}, [key]);
|
|
|
|
// Remove value from localStorage and reset to initial value
|
|
const removeValue = useCallback(() => {
|
|
try {
|
|
if (typeof window !== 'undefined') {
|
|
window.localStorage.removeItem(key);
|
|
}
|
|
const resetValue = initialValue instanceof Function ? initialValue() : initialValue;
|
|
setStoredValue(resetValue);
|
|
} catch (error) {
|
|
handleError(error, `LocalStorage Remove (key: ${key})`);
|
|
}
|
|
}, [key, initialValue]);
|
|
|
|
return [storedValue, setValue, removeValue];
|
|
}
|