Lesson 10. Using contexts to share values between components 1. The provider/consumer pattern
When using contexts, it is most of the time to use the “provider/consumer” pattern. As its name suggests, this pattern is composed of two main elements: a provider whose goal is to provide a particular value, and consumers, the components that will consume this value.
Some libraries that you can use with React use this pattern: Redux, Auth0,
MaterialUI, etc. Usually, you encapsulate your main component inside a
Provider component, and then in the child components you can use functions of
the library:
// Main component:return (<Provider params={someParams}><App /></Provider>)// In App or a child component:const value = getValueFromProvider()
Let’s use this pattern to implement a theming feature for our application. We
want to have access in every component of the application to a theme object
containing our theme’s colors.
First step, we need to create the context:
import React, { createContext } from 'react'const themeContext = createContext()
This context brings us a default Provider to which we would need to pass a
prop the initial value of the context. Since we want to provide an initial theme
by default, let’s create our own Provider component, encapsulating the default
one:
const ThemeProvider = ({children,background = 'white',foreground = 'black',}) => {return (<themeContext.Provider value={{ theme: { background, foreground } }}>{children}</themeContext.Provider>)}
Note that the context's value isn’t just the theme but an object with a theme
attribute. It will help us in a moment to store other things than the theme in
the same context.
We can now write the main component of our application, using this provider:
const App = () => {return (<ThemeProvider foreground="green"><Label>Hello</Label></ThemeProvider>)}
Now that we handled the provider let’s see how we can consume the context’s
value. Several ways are possible: we could use the Context component provided
by the context to use render-props, but the easiest possible way is, you guessed
it, using hooks.
React provides a useContext hook, which returns the current value of a
context. It means that we can get our theme in any component (assuming it is
rendered as a child component of ThemeProvider) by writing:
const { theme } = useContext(themeContext)
But do we want the user to be aware of the whole themeContext object? The user
only cares about the theme itself, so we can create a custom hook to return only
the theme:
const useTheme = () => {const { theme } = useContext(themeContext)return theme}
We can now use this hook useTheme to get the current context’s value. Here is
the final code defining the context, the provider, the hook, and the component
using the context:
Result