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.



image



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.



Thank.



All Articles