Using Context API in React to create a global application theme

Hi, this is my first post on Habr. I hope you will be interested.

So, I want to start a series of posts directly or indirectly related to the creation of ui-kit.


The objective of this post: Find a solution to control the theme of an application whose components are implemented in React.js . We will use two global themes - dark and light .

In this example, I will use the create-react-context module to create the context.

We start by creating a folder in the project root ( src / ) called theme-context . The structure of this folder will look like this:

 theme-context/ ThemeConsumer/ ThemeConsumer.js index.js ThemeProvider/ ThemeProvider.js index.js constants.js context.js index.js 

Personally, I always start with the index.js file. You do all the imports and exports at first, and then your head does not hurt about them.

theme-context / index.js

 export { ThemeProvider } from './ThemeProvider'; export { ThemeConsumer } from './ThemeConsumer'; 

theme-context / ThemeConsumer / index.js

 export { ThemeConsumer } from './ThemeConsumer'; 

theme-context / ThemeProvider / index.js

 export { ThemeProvider } from './ThemeProvider'; 

theme-context / context.js

Next, we will create a context using createContext (sorry for the pun), using the module from here .

 import createContext from 'create-react-context'; const { Provider, Consumer } = createContext(); export { Provider, Consumer }; 

Import createContext , destruct it into Provider and Consumer , and export them.

theme-context / constants.js

Everything is simple here, we create our variables so as not to pollute the main files.

 export const themeLight = 'light'; export const themeDark = 'dark'; export const defaultTheme = themeLight; export const themes = [themeLight, themeDark]; 

As I said earlier, our application will have two themes - light and dark.

theme-context / ThemeProvider / ThemeProvider.js

Here we will talk about the provider - the component that is available in each React.Context object. It allows consumers to listen and respond to changing contexts.

In our example, the prop is a theme that will be transferred to all flows of this Provider a.

 import React from 'react'; import { Provider } from '../context'; import { defaultTheme, themes } from '../constants'; function ThemeProvider({ theme, children }) { return <Provider value={theme}>{children}</Provider>; } export { ThemeProvider }; 

theme-context / ThemeConsumer / ThemeConsumer.js

In this file we will work with Consumer - this is a component that “listens, waits” for a context change. The child of this component is a function. This is a must when using Consumer .

This function receives the values ​​of the current context and returns a React Node , in other words, a component.

From the documentation: the value of the argument (in our case {theme => / * to visualize something based on the context value * /}) will be equal to the props theme closest parent in the Provider tree for this context.

 import React from 'react'; import { defaultTo } from 'lodash'; import { Consumer } from '../context'; import { defaultTheme, themes } from '../constants'; function ThemeConsumer(props) { return <Consumer>{theme => props.children(defaultTo(theme, props.defaultTheme))}</Consumer>; } export { ThemeConsumer }; 

Here it is worth paying attention to the following:
If the theme was not selected explicitly, we need the theme of the components to be selected automatically, for this I use the lodash from lodash - defaultTo . However, this functionality can be achieved in many other ways.

That's it, the theme context is ready to use!

Let's look at how to apply it. Let's create a simple component that will listen and respond to the context of our application.

 .my-class { font-family: sans-serif; text-align: center; font-size: 30px; } .my-class-light { color: #39cccc; } .my-class-dark { color: #001a33; } 

 import React from "react"; import ReactDOM from "react-dom"; import cx from "classnames"; import { ThemeConsumer, ThemeProvider } from "./theme-context"; import "./styles.css"; function MyComponent() { const renderMyComponent = theme => { const myComponentClassName = cx("my-class", { "my-class-dark": theme === "dark", "my-class-light": theme === "light" }); return ( <div className={myComponentClassName}> <h1>    </h1> </div> ); }; return <ThemeConsumer>{theme => renderMyComponent(theme)}</ThemeConsumer>; }; function App() { return ( <MyComponent /> ); } const rootElement = document.getElementById("root"); ReactDOM.render( //      theme  dark <ThemeProvider theme="light"> <App /> </ThemeProvider> , rootElement); 

So, we wrapped our <App /> in the provider, and now the theme has become available to all components-consumers in our application. Next, <App /> returns <MyComponent /> , this is , and it will create our component and pass it the theme of our application. And already having a topic as an argument:

 <ThemeConsumer>{theme => renderMyComponent(theme)}</ThemeConsumer> 

we can use it when creating a component
  const renderMyComponent = theme => { //      }; 

The working code can be found here .

That's all, I hope you find this post useful. In the next post I will try to create a media-context , the functionality of which will help us visualize the components on the equipment of the user’s device.



All Articles