useEffect.dev
← Back to lessons

Lesson 10. Using contexts to share values between components 2. Updating a context value

Our theme context is nice, but it only returns static values. What if we want to update some of the context’s values? Here we will improve our context to handle two themes: a light one and a dark one. What we want is to offer the possibility of switching between the light and the dark theme.

If you look at our ThemeProvider, you’ll notice that it’s actually just a React component: instead of using a static theme, nothing prevents us from using a local state to store a theme that can be updated.

We will keep two variables for the different themes, and a local state will contain one of the two variables. By declaring a toggle function and putting it into the context value, we can provide the ability to switch from one theme to the other:

const lightTheme = { background: 'white', foreground: 'black' }
const darkTheme = { background: 'black', foreground: 'white' }
const ThemeProvider = ({ children, initialTheme = lightTheme }) => {
const [theme, setTheme] = useState(initialTheme)
const toggle = () => {
setTheme(theme === lightTheme ? darkTheme : lightTheme)
}
return (
<themeContext.Provider value={{ theme, toggle }}>
{children}
</themeContext.Provider>
)
}

Our context value contains two attributes: one for the theme and one for the function toggle.

To access this toggle function in consumer components, of course we could use the same hook useTheme, which could return both the theme and the update function. But since custom hooks provide us a way to create small hooks dedicated to a specific task, let’s create a new hook specifically to get this toggle function:

const useToggleTheme = () => {
const { toggle } = useContext(themeContext)
return toggle
}

We can now create a ThemeToggler component using this hook, and insert it wherever we want in the component hierarchy:

const ThemeToggler = () => {
const toggle = useToggleTheme()
return <button onClick={() => toggle()}>Toggle</button>
}
const App = () => {
return (
<ThemeProvider>
<Label>Hello</Label>
<ThemeToggler />
</ThemeProvider>
)
}

Here is the final code with all the puzzle’s pieces:

Result

Note that for a component to use a context, it doesn’t have to be a direct child of the provider. You can use it very deep in the components hierarchy, and this is the advantage of the contexts compared to props passed from parent to children components.