Effect CommunityEC
Effect Community3mo ago
3 replies
makisuo

Removing useEffect with Effect Atom

Getting a bit into using Effect Atom now and loving it so far.
Is there a way to get rid of the useEffect here and do it as a Atom sideeffect or similar?


import { BrowserKeyValueStore } from "@effect/platform-browser"
import { Atom, useAtomSet, useAtomValue } from "@effect-atom/atom-react"
import { Schema } from "effect"
import { useEffect } from "react"

export type Theme = "dark" | "light" | "system"

type ThemeProviderProps = {
    children: React.ReactNode
    defaultTheme?: Theme
    storageKey?: string
}

const ThemeSchema = Schema.Literal("dark", "light", "system")


const localStorageRuntime = Atom.runtime(BrowserKeyValueStore.layerLocalStorage)


export const themeAtom = Atom.kvs({
    runtime: localStorageRuntime,
    key: "hazel-ui-theme",
    schema: Schema.NullOr(ThemeSchema),
    defaultValue: () => "system" as const,
})

export const resolvedThemeAtom = Atom.make((get) => {
    const theme = get(themeAtom)
    if (theme === "system") {
        if (typeof window !== "undefined") {
            return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
        }
        return "light"
    }
    return theme || "system"
})

export function ThemeProvider({ children }: ThemeProviderProps) {
    const resolvedTheme = useAtomValue(resolvedThemeAtom)

    useEffect(() => {
        const root = window.document.documentElement

        root.classList.remove("light", "dark")
        root.classList.add(resolvedTheme)
    }, [resolvedTheme])

    return <>{children}</>
}

export const useTheme = () => {
    const theme = useAtomValue(themeAtom)
    const setTheme = useAtomSet(themeAtom)

    return {
        theme: theme || "system",
        setTheme,
    }
}
Was this page helpful?