Dark Mode Toggle po naszemu: 5 wzorców lazy initialization w React
Rozszerzamy dokumentację useState o przypadki SSR, cookies, A/B testy i magazyny przeglądarki, aby uniknąć migotania motywu.
1. Dlaczego lazy initialization?
Reactowe useState przyjmuje funkcję inicjalizującą, która wykonuje się tylko raz podczas pierwszego renderu. Dokumentacja (docs/react-useState-lazy-initialization.md) pokazuje, że to świetny sposób na pobranie motywu z localStorage i uniknięcie dodatkowych renderów. Rozszerzmy tę technikę o inne scenariusze.
2. Standardowy Dark Mode (localStorage)
const [isDark, setIsDark] = useState(() => {
const saved = localStorage.getItem("theme");
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
return saved === "dark" || (!saved && prefersDark);
});
Pierwszy render od razu zna poprawny motyw, więc nie ma migotania.
3. SSR + Hydration
Na serwerze nie mamy window. Zabezpiecz się przed tym:
const [isDark, setIsDark] = useState(() => {
if (typeof window === "undefined") {
return false; // domyślny motyw dla SSR
}
const saved = localStorage.getItem("theme");
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
return saved === "dark" || (!saved && prefersDark);
});
4. Cookies (np. multi-device sync)
import Cookies from "js-cookie";
const [isDark, setIsDark] = useState(() => {
if (typeof window === "undefined") return false;
const cookie = Cookies.get("theme");
if (cookie) return cookie === "dark";
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
return prefersDark;
});
useEffect(() => {
Cookies.set("theme", isDark ? "dark" : "light", { expires: 365 });
}, [isDark]);
Cookie synchronizuje motyw między przeglądarkami i urządzeniami.
5. sessionStorage (formularze)
const [isDark, setIsDark] = useState(() => {
if (typeof window === "undefined") return false;
const saved = sessionStorage.getItem("theme");
return saved ? saved === "dark" : false;
});
useEffect(() => {
sessionStorage.setItem("theme", isDark ? "dark" : "light");
}, [isDark]);
Przydatne w panelach administracyjnych, gdzie chcesz pamiętać motyw na czas sesji.
6. A/B test – przechowywanie wariantu
const getVariant = () => {
if (typeof window === "undefined") return "control";
const stored = localStorage.getItem("theme_variant");
if (stored) return stored;
const variant = Math.random() > 0.5 ? "control" : "experiment";
localStorage.setItem("theme_variant", variant);
return variant;
};
const [variant] = useState(getVariant);
const initialDark = variant === "experiment";
const [isDark, setIsDark] = useState(() => {
if (typeof window === "undefined") return initialDark;
const saved = localStorage.getItem("theme");
return saved ? saved === "dark" : initialDark;
});
Wariant decyduje o domyślnym motywie, a lazy init gwarantuje, że wylosujesz go tylko raz.
7. SSR + klastry CDN (wersja Hermes)
Jeśli odczytujesz dane z window.__INITIAL_DATA__, użyj lazy init, by nie dekodować JSON na każdym renderze:
const [initialTheme] = useState(() => {
if (typeof window === "undefined") return "light";
const data = (window as any).__INITIAL_DATA__;
return data?.theme ?? "light";
});
const [isDark, setIsDark] = useState(() => initialTheme === "dark");
8. Podsumowanie
- Zawsze otaczaj kod warunkiem
typeof window !== "undefined"w środowisku SSR. - Lazy initialization zapobiega migotaniu i zbędnym renderom.
- Zmieniaj storage (
localStorage,sessionStorage, cookies) zależnie od potrzeb biznesowych. - Przetwarzanie A/B i danych wstępnych (SSR) to kolejne przypadki, w których funkcja inicjalizująca robi różnicę.
Usprawnione zarządzanie motywem to wyższy komfort użytkownika i mniej pracy nad łagodzeniem efektów ubocznych w interfejsie.
📚 Dokumentacja i Zasoby
Oficjalna Dokumentacja
Powiązane Artykuły
Podobał Ci się ten tutorial?
Podziel się nim ze znajomymi i kolegami, którym może się przydać!
📚 Powiązane Artykuły
AI w pipeline frontendu: Plan Mode + hook PostToolUse dla pełnej automatyzacji
Jak skonfigurować Claude Code, by po zmianach komponentów AI planował zadania, formatował kod, uruchamiał astro check oraz wskazywał brakujące testy.
Hooki Claude Code jako CI light: lint, astro check, testy i Lighthouse
Budujemy zestaw hooków PostToolUse i Stop, które po każdej zmianie formatują kod, uruchamiają testy, generują coverage oraz raport dostępności.