UNPKG

react-router-theme

Version:
85 lines (64 loc) 2.68 kB
import { useEffect, useState } from "react"; /** * See Readme: https://www.npmjs.com/package/react-router-theme */ export const useTheme = (loaderData: any, fetcher: any, defaultTheme?: string) => { let initialTheme: string; if (!loaderData || !("theme" in loaderData)) { throw new Error("Provided loader data does not contain theme."); } if (typeof loaderData.theme === "string") { initialTheme = loaderData.theme; } else if (loaderData.theme === null) { initialTheme = defaultTheme ?? "default"; } else { throw new Error("Provider loader data contains an invalid value for theme."); } const [theme, setTheme] = useState<string>(initialTheme); const changeTheme = (theme: string) => { setTheme(theme); localStorage.setItem("theme", theme); fetcher.submit({ action: "themeChange", theme: theme }, { method: "POST" }); }; // Sync theme updates across windows and tabs using local storage const localStorageEventHandler = (event: StorageEvent) => { if (event.key !== "theme" || event.oldValue === event.newValue || event.newValue === null) return; setTheme(event.newValue); }; useEffect(() => { window.addEventListener("storage", localStorageEventHandler); return () => window.removeEventListener("storage", localStorageEventHandler); }, []); return [theme, changeTheme] as const; }; /** * @param req incoming request in loader * @returns the value of the theme cookie if found, otherwise NULL */ export const getThemeFromCookie = (req: Request) => { const cookieHeader = req.headers.get("Cookie"); if (!cookieHeader) return null; const themeMatch = cookieHeader.match(/theme=([^;]+)/); if (!themeMatch) return null; return themeMatch[1]; }; export const createThemeCookie = (formData: FormData) => { const theme = formData.get("theme"); if (!theme) throw new Error("No theme specified in action form data"); return `theme=${theme}; Path=/; Max-Age=31536000`; }; export const loader = async (args: { request: Request }) => { return { theme: getThemeFromCookie(args.request) }; }; export const action = async (args: { request: Request }) => { const formData = await args.request.formData(); if (formData.get("action") === "themeChange") return themeCookieResponse(formData); return new Response("Unknown action. Create a custom action function to handle non-theme related requests.", { status: 500 }); }; export const themeCookieResponse = (formData: FormData) => { return new Response(null, { headers: { "Set-Cookie": createThemeCookie(formData), }, }); };