Реализация темного режима в приложениях React с использованием стилизованных компонентов
Опубликовано: 2022-03-10Одной из наиболее часто запрашиваемых программных функций является темный режим (или ночной режим, как его называют другие). Мы видим темный режим в приложениях, которые используем каждый день. От мобильных до веб-приложений темный режим стал жизненно важным для компаний, которые хотят заботиться о глазах своих пользователей.
Темный режим — это дополнительная функция, которая отображает в пользовательском интерфейсе в основном темные поверхности. Большинство крупных компаний (например, YouTube, Twitter и Netflix) внедрили темный режим в свои мобильные и веб-приложения.
Хотя мы не будем углубляться в React и стилизованные компоненты, вам пригодятся базовые знания React, CSS и стилизованных компонентов. Это руководство будет полезно тем, кто хочет улучшить свои веб-приложения, ориентируясь на тех, кто любит темный режим.

За несколько дней до написания этой статьи StackOverflow объявил о выпуске темного режима, который дает пользователям возможность переключаться между двумя режимами.
Темный режим снижает нагрузку на глаза и помогает при длительной работе за компьютером или мобильным телефоном.
Что такое темный режим?
Темный режим — это цветовая схема любого интерфейса, которая отображает светлый текст и элементы интерфейса на темном фоне, что делает экран немного удобнее для просмотра на мобильных телефонах, планшетах и компьютерах. Темный режим уменьшает свет, излучаемый экраном, сохраняя при этом минимальные соотношения цветов и контрастности, необходимые для удобочитаемости.
Почему вы должны заботиться о темном режиме?
Темный режим улучшает визуальную эргономику, снижая нагрузку на глаза, настраивая экран в соответствии с текущими условиями освещения и обеспечивая простоту использования ночью или в темноте.
Прежде чем внедрять темный режим в наше приложение, давайте посмотрим на его преимущества.
Экономия батареи
Темный режим в веб-приложениях и мобильных приложениях может продлить срок службы аккумулятора устройства. Google подтвердил, что темный режим на OLED-экранах значительно увеличил время автономной работы.
Например, при яркости 50 % темный режим в приложении YouTube экономит примерно на 15 % больше энергии экрана, чем плоский белый фон. При 100% яркости экрана темный интерфейс экономит колоссальные 60% энергии экрана.
Темный режим прекрасен
Темный режим прекрасен и может значительно повысить привлекательность экрана.
В то время как большинство продуктов имеют такой же мягкий белый вид, темный режим предлагает что-то другое, что кажется загадочным и новым.
Он также предоставляет отличные возможности для представления графического содержимого, такого как информационные панели, изображения и фотографии, по-новому.

Теперь, когда вы знаете, почему вы должны реализовать темный режим в своем следующем веб-приложении, давайте углубимся в стилизованные компоненты, которые являются определяющим ресурсом этого руководства.
Темный режим — это цветовая схема любого интерфейса, при которой светлый текст и элементы интерфейса отображаются на темном фоне, что немного облегчает просмотр на мобильных телефонах, планшетах и компьютерах.
“
Что такое стилизованные компоненты?
В этой статье мы будем очень часто использовать библиотеку styled-components. Всегда было много способов стилизовать современное веб-приложение. Существует традиционный метод стилизации на уровне документа, который включает создание файла index.css и связывание его с HTML или стилем внутри файла HTML.
В последнее время многое изменилось в стилях веб-приложений с момента появления CSS-in-JS.
CSS-in-JS относится к шаблону, в котором CSS составлен с использованием JavaScript. Он использует помеченные литералы шаблонов для стилизации компонентов в файле JavaScript.
Чтобы узнать больше о CSS-in-JS, ознакомьтесь со статьей Анны Монус на эту тему.
styled-components — это библиотека CSS-in-JS, позволяющая использовать все функции CSS, которые вам нравятся, включая медиазапросы, псевдоселекторы и вложенность.
Почему стилизованные компоненты?
styled-components был создан по следующим причинам:
- Ад без имени класса
Вместо того, чтобы ломать голову над поиском имени класса для элемента, styled-components генерирует уникальные имена классов для ваших стилей. Вам никогда не придется беспокоиться об ошибках в написании или использовании имен классов, которые не имеют смысла. - Использование реквизита
styled-components позволяют нам расширять свойства стиля с помощью параметраprops, обычно используемого в React, — таким образом, динамически влияя на ощущение компонента через состояние приложения. - Поддерживает синтаксис Sass.
Написание синтаксиса Sass из коробки без необходимости установки каких-либо препроцессоров или дополнительных инструментов сборки возможно с помощью styled-components. В определениях стилей вы можете использовать символ&для выбора текущего компонента, использовать псевдоселекторы и экспериментировать с вложенностью. - Тематика
styled-components имеют полную поддержку тем за счет экспорта компонента-оболочкиThemeProvider. Этот компонент предоставляет тему для всех компонентов React внутри себя через Context API. В дереве рендеринга все стилизованные компоненты будут иметь доступ к предоставленной теме, даже если они имеют несколько уровней глубины. По мере того, как мы продолжим работу с этим руководством, мы углубимся в тематические функции styled-components.
Чтобы узнать больше о преимуществах styled-components, ознакомьтесь со статьей Криса Гузмана.
Реализация темного режима
В этой статье мы собираемся реализовать темный режим на простой веб-странице, похожей на YouTube.
Чтобы продолжить, убедитесь, что вы клонируете исходный репозиторий из starter ветки.
Настройка
Давайте установим все зависимости в наш файл package.json . В терминале выполните следующую команду:
npm install После успешной установки запустите npm start . Вот как выглядит веб-страница без реализованного на ней темного режима.

Чтобы установить styled-components , в терминале запустите npm install styled-components .
Реализация
Чтобы реализовать темный режим, нам нужно создать четыре разных компонента.
-
Theme
Он содержит цветовые свойства наших светлых и темных тем. -
GlobalStyles
Он содержит глобальные стили для всего документа. -
Toggler
Это содержит элемент кнопки, который переключает функциональность. -
useDarkMode
Этот пользовательский хук обрабатывает логику смены темы и сохранение нашей темы в localStorage.
Компонент темы
В папке src вы увидите компоненты в папке components . Создайте файл Themes.js и добавьте в него следующий код.
export const lightTheme = { body: '#FFF', text: '#363537', toggleBorder: '#FFF', background: '#363537', } export const darkTheme = { body: '#363537', text: '#FAFAFA', toggleBorder: '#6B8096', background: '#999', } Здесь мы определили и экспортировали lightTheme и darkTheme с различными цветовыми переменными. Не стесняйтесь экспериментировать и настраивать переменные в соответствии с вашими потребностями.
Компонент globalStyles
Оставаясь в папке components , создайте файл globalStyles.js и добавьте следующий код:
import { createGlobalStyle} from "styled-components" export const GlobalStyles = createGlobalStyle` body { background: ${({ theme }) => theme.body}; color: ${({ theme }) => theme.text}; font-family: Tahoma, Helvetica, Arial, Roboto, sans-serif; transition: all 0.50s linear; } ` Мы импортировали createGlobalStyle из styled-components. Метод createGlobalStyle заменяет уже устаревший метод injectGlobal из styled-components версии 3. Этот метод генерирует компонент React, который при добавлении в ваше дерево компонентов будет внедрять в документ глобальные стили, в нашем случае App.js
Мы определили компонент GlobalStyle и присвоили свойства background и color значениям из объекта темы. Таким образом, каждый раз, когда мы переключаем переключатель, значения будут меняться в зависимости от объектов темной темы или светлой темы, которые мы передаем в ThemeProvider (которые будут созданы позже, по мере продвижения).
Свойство перехода 0.50s позволяет этому изменению происходить немного более плавно, так что, когда мы переключаемся назад и вперед, мы можем видеть, как происходят изменения.
Создание функциональности переключения тем
Чтобы реализовать функцию переключения тем, нам нужно добавить всего несколько строк кода. В файл App.js добавьте следующий код (обратите внимание, что вы должны добавить выделенный код):
import React, { useState, useEffect } from "react";import {ThemeProvider} from "styled-components"; import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes"import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);const [theme, setTheme] = useState('light'); const themeToggler = () => { theme === 'light' ? setTheme('dark') : setTheme('light') }useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return (<ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}> <> <GlobalStyles/><div className="App"><button onClick={themeToggler}>Switch Theme</button>{ videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div></> </ThemeProvider>); }; export default App;
Выделенный код — это код, недавно добавленный в App.js Мы импортировали ThemeProvider из styled-components . ThemeProvider — это вспомогательный компонент в библиотеке styled-components, обеспечивающий поддержку тем. Этот вспомогательный компонент внедряет тему во все компоненты React ниже себя через Context API.
В дереве рендеринга все стилизованные компоненты будут иметь доступ к предоставленной теме, даже если они имеют несколько уровней глубины. Ознакомьтесь с разделом «Тематика».
Затем мы импортируем оболочку GlobalStyle из ./components/Globalstyle . Наконец, сверху мы импортируем объекты lightTheme и darkTheme из ./components/Themes .

Чтобы мы могли создать метод переключения, нам нужно состояние, которое содержит начальное значение цвета нашей темы. Итак, мы создаем состояние theme и устанавливаем начальное состояние на light с помощью хука useState .
Теперь о функции переключения.
Метод themeToggler использует тернарный оператор для проверки состояния theme и переключает темную или светлую тему в зависимости от значения условия.
ThemeProvider , вспомогательный компонент styled-components, оборачивает все в оператор return и внедряет любые компоненты ниже него. Помните, что наши GlobalStyles внедряют глобальные стили в наши компоненты; следовательно, он вызывается внутри компонента-оболочки ThemeProvider .
Наконец, мы создали кнопку с событием onClick , которое присваивает ей наш метод themeToggler .
Давайте посмотрим на результат до сих пор.

Наш файл App.js нуждается в рефакторинге; большая часть его кода не DRY. (DRY расшифровывается как «не повторяйся», основной принцип разработки программного обеспечения, направленный на сокращение повторений.) Кажется, что вся логика находится в App.js ; хорошей практикой является разделение нашей логики для ясности. Итак, мы создадим компонент, который обрабатывает функциональность переключения.
Переключить компонент
Находясь в папке components , создайте файл Toggler.js и добавьте в него следующий код:
import React from 'react' import { func, string } from 'prop-types'; import styled from "styled-components" const Button = styled.button` background: ${({ theme }) => theme.background}; border: 2px solid ${({ theme }) => theme.toggleBorder}; color: ${({ theme }) => theme.text}; border-radius: 30px; cursor: pointer; font-size:0.8rem; padding: 0.6rem; } \`; const Toggle = ({theme, toggleTheme }) => { return ( <Button onClick={toggleTheme} > Switch Theme </Button> ); }; Toggle.propTypes = { theme: string.isRequired, toggleTheme: func.isRequired, } export default Toggle; Чтобы все было аккуратно, мы стилизовали нашу кнопку-переключатель в компоненте Toggle , используя styled функцию из styled-components.
Это чисто для презентации; вы можете стилизовать кнопку по своему усмотрению.
Внутри компонента Toggle мы передаем два реквизита:
-
themeобеспечивает текущую тему (светлая или темная); - функция
toggleThemeбудет использоваться для переключения между темами.
Затем мы возвращаем компонент Button и назначаем функцию toggleTheme onClick
Наконец, мы используем propTypes для определения наших типов, гарантируя, что наша theme является string и isRequired , а наша toggleTheme является func и isRequired .
Использование пользовательских хуков ( useDarkMode )
При создании приложения масштабируемость имеет первостепенное значение, а это означает, что наша бизнес-логика должна быть многоразовой, чтобы мы могли использовать ее во многих местах и даже в разных проектах.
Вот почему было бы здорово перенести нашу функцию переключения в отдельный компонент. Для этого мы создадим собственный кастомный хук.
Давайте создадим новый файл с именем useDarkMode.js в папке components и переместим нашу логику в этот файл с некоторыми изменениями. Добавьте в файл следующий код:
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light'); const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme && setTheme(localTheme) }, []); return [theme, themeToggler] };Мы добавили здесь несколько вещей.
-
setMode
Мы используемlocalStorageдля сохранения между сессиями в браузере. Итак, если пользователь выбрал темную или светлую тему, это то, что он получит при следующем посещении приложения или при перезагрузке страницы. Следовательно, эта функция устанавливает наше состояние и передаетthemeвlocalStorage. -
themeToggler
Эта функция использует тернарный оператор для проверки состояния темы и переключает темную или светлую тему в зависимости от истинности условия. -
useEffect
Мы реализовали хукuseEffectдля проверки монтирования компонента. Если пользователь ранее выбрал тему, мы передаем ее нашей функцииsetTheme. В итоге мы вернем нашуtheme, которая содержит выбраннуюthemeи функциюthemeTogglerдля переключения между режимами.
Я думаю, вы согласитесь, что наш темный компонент выглядит гладко.
Давайте перейдем к App.js для последних штрихов.
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components";import {useDarkMode} from "./components/useDarkMode"import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);const [theme, themeToggler] = useDarkMode(); const themeMode = theme === 'light' ? lightTheme : darkTheme;useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return (<ThemeProvider theme={themeMode}><> <GlobalStyles/> <div className="App"><Toggle theme={theme} toggleTheme={themeToggler} />{ videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
Выделенный код недавно добавлен в App.js
Во-первых, мы импортируем наш пользовательский хук, деструктурируем theme и реквизиты themeToggler и устанавливаем их с помощью функции useDarkMode .
Обратите внимание, что метод useDarkMode заменяет наше состояние theme , которое изначально было в App.js
Мы объявляем переменную themeMode , которая отображает либо светлую, либо темную тему в зависимости от состояния режима theme в данный момент.
Теперь наш компонент-оболочка ThemeProvider назначает нашу только что созданную переменную themeMode theme .
И, наконец, вместо обычной кнопки мы передаем компонент Toggle .
Помните, что в нашем компоненте Toggle мы определили и стилизовали кнопку и передали им theme и toggleTheme в качестве свойств. Итак, все, что нам нужно сделать, это передать эти свойства компоненту Toggle , который будет действовать как наша кнопка в App.js
Да! Наш темный режим установлен, и он сохраняется, не меняя цвет при обновлении страницы или переходе на новую вкладку.
Посмотрим на результат в действии:

Почти все работает хорошо, но есть одна маленькая вещь, которую мы можем сделать, чтобы сделать наш опыт великолепным. Переключитесь на темную тему и перезагрузите страницу. Вы видите, что синий цвет кнопки на короткое время загружается раньше, чем серый? Это происходит потому, что наш хук useState инициирует light тему. После этого запускается useEffect , проверяет localStorage и только потом устанавливает theme в dark . Давайте перейдем к нашему пользовательскому useDarkMode.js и добавим небольшой код:
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light');const [mountedComponent, setMountedComponent] = useState(false)const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme ? setTheme(localTheme) : setMode('light')setMountedComponent(true)}, []); return [theme, themeToggler,}, []); return [theme, themeToggler,mountedComponent]};
Выделенный код — единственный, добавленный в useDarkMode.js . Мы создали еще одно состояние с именем mountedComponent и установили значение по умолчанию в false с помощью хука useState . Затем внутри хука useEffect мы устанавливаем состояние mountedComponent в true , используя setMountedComponent . Наконец, в return массив мы включаем состояние mountedComponent .
Наконец, давайте добавим немного кода в App.js , чтобы все заработало.
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import {useDarkMode} from "./components/useDarkMode" import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);const [theme, themeToggler, mountedComponent] = useDarkMode();const themeMode = theme === 'light' ? lightTheme : darkTheme; useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []);if(!mountedComponent) return <div/>return ( <ThemeProvider theme={themeMode}> <> <GlobalStyles/> <div className="App"> <Toggle theme={theme} toggleTheme={themeToggler} /> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
Мы добавили состояние mountedComponent в качестве реквизита в хук useDarkMode и проверили, смонтировался ли наш компонент, потому что именно это происходит в useEffect . Если этого еще не произошло, то будем рендерить пустой div .
Давайте посмотрим на результат нашей веб-страницы в темном режиме.

Теперь вы заметите, что в темном режиме при перезагрузке страницы цвет кнопки не меняется.
Заключение
Темный режим все чаще становится предпочтением пользователей, и реализовать его в веб-приложении React намного проще, если использовать оболочку темы ThemeProvider в styled-components. Продолжайте экспериментировать со стилизованными компонентами при реализации темного режима; вы можете добавить значки вместо кнопки.
Пожалуйста, поделитесь своими отзывами и опытом использования функции тем в styled-components в разделе комментариев ниже. Я хотел бы увидеть, что вы придумали!
Вспомогательный репозиторий для этой статьи доступен на GitHub. Кроме того, проверьте это на CodeSandbox.
использованная литература
- «Документация», styled-components
- «Создайте темный режим вашего приложения с помощью стилизованных компонентов», Том Нолан, Medium
