Reputation: 188
I'm trying to create a simple Context object that will hold some global state in my Fresh / Preact app. The problem I am having is that when trying to render the app it throws an error: Converting circular structure to JSON. I understand what this error means and why it happens (Because AppContext.Provider has a reference to itself, which causes the error when trying to stringify it, as Fresh uses server-side-rendering). What I don't know is how to fix this issue. I've tried using only Preact signals for state instead, but this caused other issues. What I really want is some way to make the solution with Context to work, as I'm comfortable using it from Next.JS.
AppContext.tsx:
import { createContext } from "preact"
import { useContext, useState } from "preact/hooks"
export type Theme = "light" | "dark"
export type Language = "english" | "norsk"
interface IAppContext {
theme: Theme,
language: Language,
toggleTheme: () => void,
toggleLanguage: () => void
}
const AppContext = createContext<IAppContext>({
theme: "light",
language: "english",
toggleLanguage: () => {},
toggleTheme: () => {}
})
export const useAppContext = () => useContext(AppContext)
export default function AppContextProvider({ children }: any) {
const [theme, setTheme] = useState<Theme>("light")
const [language, setLanguage] = useState<Language>("english")
const toggleTheme = () => setTheme(_theme => _theme === "light" ? "dark" : "light")
const toggleLanguage = () => setLanguage(_language => _language === "english" ? "norsk" : "english")
const context: IAppContext = {
theme,
language,
toggleTheme,
toggleLanguage
}
return <AppContext.Provider value={context}>{children}</AppContext.Provider>
}
Upvotes: 2
Views: 324
Reputation: 188
I ended up abandoning the Context hook and going with signals. I now simply import AppContext wherever I need it:
import { Signal, signal } from "@preact/signals"
export type Theme = "light" | "dark"
export type Language = "english" | "norsk"
interface IAppContext {
theme: Signal<Theme>,
language: Signal<Language>,
toggleTheme: () => void,
toggleLanguage: () => void
}
const theme = signal<Theme>("light")
const language = signal<Language>("english")
const storedTheme = localStorage.getItem("theme") as Theme | null
if (storedTheme) {
theme.value = storedTheme
setCSSTheme(storedTheme)
}
function toggleTheme() {
const newTheme = theme.value = theme.value === "light" ? "dark" : "light"
theme.value = newTheme
localStorage.setItem("theme", newTheme)
setCSSTheme(newTheme)
}
function setCSSTheme(theme: Theme) {
const classList = document.documentElement.classList
classList.add(theme)
classList.remove(theme === "light" ? "dark" : "light")
}
const toggleLanguage = () => language.value = language.value === "english" ? "norsk" : "english"
export const AppContext: IAppContext = {
theme,
language,
toggleTheme,
toggleLanguage
}
Upvotes: 3