В данном примере я также буду использовать модуль create-react-context
.
Дерево задания будет идентичным с предыдущим. Назовем новую папку media-context.
media-context/ MediaConsumer/ MediaConsumer.js index.js MediaProvider/ MediaProvider.js index.js constants.js context.js index.js
Быстренько разберемся с index’ами.
media-context/index.js
export { MediaProvider } from './MediaProvider'; export { MediaConsumer } from './MediaConsumer';
theme-context/MediaConsumer/index.js
export { MediaConsumer } from './MediaConsumer';
theme-context/MediaProvider/index.js
export { MediaProvider } from './MediaProvider';
theme-context/context.js
Как и ранее, создаем createContext
(модуль нпм).
import createContext from 'create-react-context'; const { Provider, Consumer } = createContext(); export { Provider, Consumer };
Импортируем, деструктурируем и экспортируем полученные компоненты.
media-context/constants.js
Переменные сохраним тут.
export const devices = ["mobile", "tablet", "desktop", "native"]; export const defaultDevice = "mobile";
В адаптивном подходе юзер в основном использует 4 вида дивайсов — настольный, планшет, мобильный и нативное приложение (native). Другие возможные устройства: смарт-тв, часы и т.п. В данном примере будем использовать только mobile & desktop.
theme-context/MediaProvider/MediaProvider.js
Как мы уже знаем, в React.Context
доступен Provider, который «кормит» консюмеров контекстом. Он позволяет им слушать и реагировать на изменения контекста.
Props Провайдера
— device
и touchable
. Напомню, что все значения переданные в value
провайдера станут доступными для консюмеров.
import React from "react"; import PropTypes from "prop-types"; import { Provider } from "../context"; import { devices, defaultDevice } from "../constanst"; function MediaProvider(props) { const device = devices.includes(props.device) ? props.device : props.defaultDevice; return <Provider value={{ ...props, device }}>{props.children}</Provider>; } MediaProvider.propTypes = { device: PropTypes.oneOf(devices), touchable: false, children: PropTypes.node }; MediaProvider.defaultProps = { device: defaultDevice, touchable: false }; export { MediaProvider };
theme-context/MediaConsumer/MediaConsumer.js
Описание функциональности консюмера можно найти в предыдущей статье, но если вкратце, то Consumer
реагирует на контекс и возвращает новый компонент.
В данном случае мы передаем два параметра в children
: вид дивайса и является ли устройство сенсорным.
import React from "react"; import PropTypes from "prop-types"; import { defaultTo } from "lodash"; import { devices, defaultDevice } from "../constanst"; import { Consumer } from '../context'; function MediaConsumer(props) { return ( <Consumer> {media => props.children({ touchable: defaultTo(media.touchable, props.defaultTouchable), device: defaultTo(media.device, props.defaultDevice), })} </Consumer> ) } MediaConsumer.propTypes = { defaultTouchable: PropTypes.bool, defaultDevice: PropTypes.oneOf(devices), children: PropTypes.func.isRequired }; MediaConsumer.defaultProps = { defaultDevice, defaultTouchable: false }; export { MediaConsumer };
Теперь в нашем приложении доступен новый контекст, давайте им воспользуемся.
import React from "react"; import ReactDOM from "react-dom"; import { isMobile } from "react-device-detect"; import cx from "classnames"; import { ThemeConsumer, ThemeProvider } from "./theme-context"; import { MediaConsumer, MediaProvider } from "./media-context"; import "./styles.css"; function renderSVG({ device, touchable }) { if (device === "mobile" && touchable) { return "https://image.flaticon.com/icons/svg/124/124114.svg"; } if (device === "tablet" && touchable) { return "https://image.flaticon.com/icons/svg/124/124099.svg"; } if (device === "desktop") { return "https://image.flaticon.com/icons/svg/124/124092.svg"; } } function MyComponent() { const renderMyComponent = (theme, media) => { const myComponentClassName = cx("my-class", { "my-class-dark": theme === "dark", "my-class-light": theme === "light" }); return ( <div className="wrapperDiv"> <object className={myComponentClassName} data={renderSVG(media)} type="image/svg+xml" /> </div> ); }; return ( <MediaConsumer> {media => ( <ThemeConsumer> {theme => renderMyComponent(theme, media)} </ThemeConsumer> )} </MediaConsumer> ); } function App() { return ( <MediaProvider device={isMobile ? "mobile" : "desktop"} touchable={isMobile} > <ThemeProvider theme="light"> <MyComponent /> </ThemeProvider> </MediaProvider> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
Для использования медиаконтеста нам потребуется медиадетектор (для получения информации о дивайсе юзера). В нашем приложении медиадетектора нет, поэтому возьмем его отсюда — react-device-detect, а именно флаг isMobile.
Как и с контекстной темой, мы обернем наш тестовый компонент в медиапровайдере. Значение пропсов, которые провайдер передаст своему консюмеру, будут зависть от устройства клиента. Зашел с мобильного, посылаем мобильную версию сайта, с компьютера — десктопную. Рабочий код можно посмотреть тут.
Чтобы увидеть разницу, зайдите по ссылке с настольного и мобильного браузера.
Все! Теперь наше приложение имеет два контекста, которые помогут нам создавать более разносторонние и гибкие компоненты в нашем UI-Kit. В следующем посту я попробую создать комплексный компонент с использованием обоих контекстов. Надеюсь, Вы найдете статью полезной.
Спасибо.
ссылка на оригинал статьи https://habr.com/ru/post/463597/
Добавить комментарий