useEffect.dev
← Back to lessons

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