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.jsNext, 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.jsEverything 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.jsHere 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.jsIn 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.