Reputation: 119
Hello 👋🏻
I have a React project with NextJS where I want to chose whether using light or dark theme depending on the background / section.
I have found that the best solution to my problem is using React Context API. To change the theme I just need to override the provider with a new one (as indicated in the documentation).
I have the start of a solution but when overriding the Provider, I noticed that the change isn't effective for every components even if in the devTools the context in theses components is the correct one. 😕
I have tried switching from useContext(context)
to <context.Consumer>
but there was no improvement.
/context/Theme
import { createContext, JSXElementConstructor, ReactElement, ReactFragment, ReactNode, ReactPortal } from "react";
import { getTheme, Theme } from "../constants/theme";
// type of getTheme: (theme: 'light' | 'dark') => Theme
const ThemeContext = createContext<Theme>(getTheme('light'))
export default ThemeContext
interface ThemeProviderProps {
theme: 'light' | 'dark'
children: string | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | ReactFragment | ReactPortal | null | undefined
}
interface ThemeConsumerProps {
children: (value: Theme) => ReactNode
}
// The goal here is to make the theme switching more natural
export const ThemeProvider = (props: ThemeProviderProps): JSX.Element => {
return (
<ThemeContext.Provider value={getTheme(props.theme)}>
{props.children}
</ThemeContext.Provider>
)
}
// Just for convenience
export const ThemeConsumer = (props: ThemeConsumerProps): JSX.Element => {
return <ThemeContext.Consumer>{props.children}</ThemeContext.Consumer>
}
/pages/_app (Starting point of NextJS App, equivalent to /src/App
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { ThemeProvider } from "../context/Theme";
export default function App({ Component, pageProps }: AppProps) {
return (
<ThemeProvider theme="light">
<Component {...pageProps} />
</ThemeProvider>
)
}
/common/Popup/Popup
import {ReactNode, useContext, useState} from "react";
import styles from "./Popup.module.css"
import Typography, {Typos} from "../Typography";
import Icon from "../Icon";
import {CloseIcon} from "../../icons";
import ThemeContext, {ThemeProvider, ThemeConsumer} from "../../context/Theme";
interface Props {
children: ReactNode
title: string
open: boolean
close: () => void
}
export default function (props: Props): JSX.Element {
return (
<>
// ...
<ThemeProvider theme="dark">
<div className={styles.header}>
<Typography level={Typos.TITLE_2} bold>{ props.title }</Typography> // The useContext works inside of this components
<ThemeConsumer>
{(theme) =>
<Icon icon={CloseIcon} color={theme.texts.secondary} size={1.5}
onClick={props.close} clickable />
}
</ThemeConsumer>
</div>
<div className={styles.body}>
{ props.children } // The useContext or the <context.Consumer> doesn't work for the children
</div>
</ThemeProvider>
// ...
</>
)
}
Upvotes: 0
Views: 886
Reputation: 119
I've just misplaced my <context.Consumer>
outside of the Popup component and I learned that useContext
doesn't work when overriding the Provider.
Upvotes: 1