Итак, я хочу начать серию постов, напрямую или косвенно касающихся создания ui-kit.

Задача данного поста: Найти решение контроля темы приложения, компоненты которого выполнены в React.js. Будем использовать две глобальные темы — dark и light.
В данном примере я буду использовать модуль create-react-context, для создания контекста.
Начнем с создания папки в корне проекта (src/) под названием theme-context. Структура данной папки будет выглядеть следующим образом:
theme-context/ ThemeConsumer/ ThemeConsumer.js index.js ThemeProvider/ ThemeProvider.js index.js constants.js context.js index.js
Лично, я всегда начинаю с файла index.js. Делаешь все импорты и экспорты вначале, и потом уже голова о них не болит.
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
Далее, мы создадим контекст при помощи createContext (простите за каламбур), используя модуль отсюда.
import createContext from 'create-react-context'; const { Provider, Consumer } = createContext(); export { Provider, Consumer };
Импортируем createContext, деструктурируем его на Provider и Consumer, и экспортируем их.
theme-context/constants.js
Здесь все просто, создаем наши переменные, чтобы не загрязнять основные файлы.
export const themeLight = 'light'; export const themeDark = 'dark'; export const defaultTheme = themeLight; export const themes = [themeLight, themeDark];
Как я и говорил ранее, у нашего приложения будет две темы — лайт и дарк.
theme-context/ThemeProvider/ThemeProvider.js
Здесь речь пойдет о провайдере — компоненте, который доступен в каждом объекте React.Context. Он позволяет консюмерам слушать и реагировать на изменения контекста.
В нашем примере, проп Провайдера — это theme, который будет передан всем Конусюмерам-потомкам данного Providerа.
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
В данном файле мы будем работать с Consumer — это компонент, который «слушает, ждет» изменения контекста. Дитя (Children) данного компонента — функция. Это обязательное требование при использовании Consumer.
Данная функция получает значения текущего контекста и возвращает React Node, проще говоря — компонент.
Из документации: значение аргумента (в нашем случае {theme => /* визуализировать что-либо на основе значения контекста */}) будет равно пропсу theme ближайшего, вышестоящего в дереве Provider для данного контекста.
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 };
Тут стоит обратить внимание на следующее:
Если тема не была выбрана эксплицитно, нам нужно, чтобы тема компонентов была выбрана автоматически, для этого я использую утиль из lodash — defaultTo. Однако добиться данной функциональности можно множеством иных способов.
Вот и все, контекст темы готов к использованию!
Давайте рассмотрим, как его применить. Создадим простой компонент, который будет слушать и реагировать на тему контекста нашего приложения.
.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);
Итак, мы обернули наш <App /> в провайдере, и теперь тема стала доступна всем компонентам-консюмерам в нашем приложении. Далее, <App /> возвращает <MyComponent />, это Консюмер, и он создаст наш компонент и передаст ему тему нашего приложения. А уже имея тему в виде аргумента:
<ThemeConsumer>{theme => renderMyComponent(theme)}</ThemeConsumer>
мы сможем использовать ее при создании компонента
const renderMyComponent = theme => { // создание компонента основываясь на теме };
Рабочий код можно посмотреть тут.
Вот и все, надеюсь, Вы найдете данный пост полезным. В следующем посту я попробую создать media-context, функциональность которого поможет нам визуализировать компоненты на оснавании девайса юзера.
Спасибо.
ссылка на оригинал статьи https://habr.com/ru/post/462963/
Добавить комментарий