feat: Add theme switch

This commit is contained in:
deytenit committed 2026-02-15 15:36:31 +03:00
1 parent 891027cab0
commit 2d1bc9d119
38 files changed
+1724 -218

No files matched your search

@@ -0,0 +1,56 @@
import { useCallback, useSyncExternalStore } from 'react';
import type { StorageManager } from '../../window/localStorage';
/**
* A React hook that synchronizes state with a StorageManager.
*
* It provides reactivity across the component lifecycle and synchronizes
* values across multiple browser tabs using the 'storage' event.
*
* @template T - The type of the stored value.
* @param manager - The StorageManager instance returned by createLocalStorage.
* @returns A tuple containing the current value and a setter function.
*
* @example
* ```tsx
* const [theme, setTheme] = useLocalStorage(themeStorage);
* ```
*/
export function useLocalStorage<T>(
manager: StorageManager<T>
): [T, (value: T | ((prev: T) => T)) => void] {
const subscribe = useCallback(
(callback: () => void) => {
const handleStorageChange = (event: StorageEvent): void => {
if (event.key === manager.key) {
callback();
}
};
window.addEventListener('storage', handleStorageChange);
return () => {
window.removeEventListener('storage', handleStorageChange);
};
},
[manager.key]
);
const getSnapshot = useCallback(() => manager.get(), [manager]);
const value = useSyncExternalStore(subscribe, getSnapshot, () => {
// During SSR, we can't access localStorage.
// However, createLocalStorage is designed to throw if accessed during SSR.
// This hook is intended to be used in client-side components.
return manager.get();
});
const setValue = useCallback(
(nextValue: T | ((prev: T) => T)) => {
manager.set(nextValue);
},
[manager]
);
return [value, setValue];
}