The final part of our theme feature is a component that can be used to change the theme dynamically, by making use of the State Hook that we defined earlier. The State Hook is going to re-render the App component, which will change the value that is passed to the ThemeContext.Provider, which, in turn, is going to re-render all the components that make use of the ThemeContext Context Hook.
Let's start implementing the ChangeTheme component:
- Create a new src/ChangeTheme.js file.
- As always, we have to import React first, before we can define a component:
import React from 'react'
- In order to be able to easily add new themes later on, we are going to create a constant THEMES array, instead of manually copying and pasting the code for the different themes. This is going to make our code much more concise, and easier to read:
const THEMES = [
{ primaryColor: 'deepskyblue', secondaryColor: 'coral' },
{ primaryColor: 'orchid', secondaryColor: 'mediumseagreen' }
]
It is a good idea to give constant values that are hardcoded a special name, such as writing the whole variable name in caps. Later on, it might make sense to put all these configurable hardcoded values in a separate src/config.js file.
- Next, we define a component to render a single theme:
function ThemeItem ({ theme, active, onClick }) {
- Here, we render a link, and display a small preview of the theme, by showing the Primary and Secondary colors:
return (
<span onClick={onClick} style={{ cursor: 'pointer', paddingLeft: 8, fontWeight: active ? 'bold' : 'normal' }}>
<span style={{ color: theme.primaryColor }}>Primary</span> / <span style={{ color: theme.secondaryColor }}>Secondary</span>
</span>
)
}
Here, we set the cursor to pointer, in order to make the element appear clickable. We could also use an <a> element; however, this is not recommended if we do not have a valid link target, such as a separate page.
- Then, we define the ChangeTheme component, which accepts the theme and setTheme props:
export default function ChangeTheme ({ theme, setTheme }) {
- Next, we define a function to check if a theme object is the currently active theme:
function isActive (t) {
return t.primaryColor === theme.primaryColor && t.secondaryColor === theme.secondaryColor
}
- Now, we use the .map function to render all of the available themes, and call the setTheme function when clicking on them:
return (
<div>
Change theme:
{THEMES.map((t, i) =>
<ThemeItem key={'theme-' + i} theme={t} active={isActive(t)} onClick={() => setTheme(t)} />
)}
</div>
)
}
- Finally, we can import and render the ChangeTheme component, after the Header component in src/App.js:
import ChangeTheme from './ChangeTheme'
// ...
return (
<ThemeContext.Provider value={theme}>
<div style={{ padding: 8 }}>
<Header text="React Hooks Blog" />
<ChangeTheme theme={theme} setTheme={setTheme} />
<br />
As we can see, we now have a way to change the theme in our app:
Our app after changing the theme, using Context Hooks in combination with a State Hook
Now, we have a context that is consumed via Hooks, which can also be changed via Hooks!