Context
Context is designed to share data that can be considered “global” for a tree of React components
— React
- e.g. current user, theme, language, settings
- props that need to be passed thru layers of components
- parent to child communication
- can be nested, closest value wins
How?
- declare the context
const ThemeCtx = createContext(defaultTheme)
- provide a value
<ThemeCtx.Provider value={dark}>…</ThemeCtx.Provider>
- use the value
const currentTheme = useContext(ThemeCtx)
import React, { createContext, FC, useContext } from 'react'
import classes from './example.module.css'
const Box: FC<{ color: string; dashed?: boolean }> = ({
color,
dashed,
children,
}) => (
<div
className={classes.box}
style={{ borderColor: color, borderStyle: dashed ? 'dashed' : 'solid' }}
>
{children}
</div>
)
const GreenDashedBox: FC = ({ children }) => (
<Box color="#50fa7b" dashed>
{children}
</Box>
)
const Context = createContext<string>('default')
const ProvidesContext: FC<{ color: string }> = ({ color, children }) => (
<Box color={color}>
<pre>providing value: `{color}`</pre>
<Context.Provider value={color}>{children}</Context.Provider>
</Box>
)
const RendersContext: FC = () => {
const value = useContext(Context)
return <pre>using value: `{value}`</pre>
}
export default (
<>
<p>default value is used when there is no provider</p>
<RendersContext />
{/* default */}
<ProvidesContext color="#ff5555">
<RendersContext />
{/* #ff5555 */}
<GreenDashedBox>
<p>value pierces component boundaries</p>
<RendersContext />
{/* #ff5555 */}
<ProvidesContext color="#8be9fd">
<p>providers can be nested</p>
<p>closest value is used</p>
<RendersContext />
{/* #8be9fd */}
</ProvidesContext>
</GreenDashedBox>
</ProvidesContext>
</>
)
default value is used when there is no provider
using value: `default`
providing value: `#ff5555`
using value: `#ff5555`
value pierces component boundaries
using value: `#ff5555`
providing value: `#8be9fd`
providers can be nested
closest value is used
using value: `#8be9fd`
Dynamic Theme
import React, { createContext, FC, useContext, useState } from 'react'
import defaultClasses from './button.default.module.css'
import greenClasses from './button.green.module.css'
import redClasses from './button.red.module.css'
export type ThemeContextType = 'default' | 'red' | 'green'
export const ThemeContext = createContext<ThemeContextType>('default')
export const Button: FC = () => {
const theme = useContext(ThemeContext)
const classes = {
default: defaultClasses,
red: redClasses,
green: greenClasses,
}[theme]
return (
<button type="button" className={classes.button}>
themed button
</button>
)
}
export const ThemeSwitcher: FC = ({ children }) => {
const [theme, setTheme] = useState('default')
return (
<>
<label>
theme
<select value={theme} onChange={(e) => setTheme(e.target.value)}>
<option>default</option>
<option>red</option>
<option>green</option>
</select>
</label>
<br />
<ThemeContext.Provider value={theme as ThemeContextType}>
{children}
</ThemeContext.Provider>
</>
)
}
export default (
<ThemeSwitcher>
<Button />
</ThemeSwitcher>
)