Чтобы установить styled-components, в терминале запустите:
npm install styled-components
Реализация Для реализации ночного режима необходимо создать четыре разных компонента.
Theme — Содержит свойства цветов светлой и тёмной тем. GlobalStyles — Содержит глобальные стили всего документа. Toggler — Содержит элемент кнопки переключения режимов. useDarkMode — Пользовательский хук, который обрабатывает логику смены темы и её сохранение в localStorag. Компонент темы В папке src вы увидите компоненты в папке components. Создайте файл Themes.js и добавьте в него следующий код:
В папке 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 с отличающимися переменными цветами. Не стесняйтесь экспериментировать и настраивать их значения в соответствии с вашими предпочтениями.
Компонент глобальных стилей Не покидая папку 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. Он генерирует 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 (
<>
Switch Theme
{
videos.map((list, index) => {
return (
);
})}
>
);
};
export default App;
Выделенный код вы должны были добавить в App.js. Мы импортировали ThemeProvider из styled-components. ThemeProvider является вспомогательным компонентом в библиотеке styled-components, которая обеспечивает поддержку тем. Этот компонент внедряет тему во все нижеуказанные компоненты React через ywwmbggHUgA 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 означает «don’t repeat yourself», основной принцип разработки ПО, направленный на уменьшение количества повторений.) Похоже, что вся логика находится в 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 (
> Switch Theme
);
};
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, которая содержит выбранную тему и функцию 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 (
<>
{
videos.map((list, index) => {
return (
);
})}
>
);
};
export default App;
Выделенный код нужно добавить в App.js.
Сначала мы импортируем наш пользовательский хук, деструктурируем пропсы theme и themeToggler и устанавливаем их с помощью функции useDarkMode.
Обратите внимание, что метод useDarkMode заменяет состояние темы, которое изначально было в App.js.
Мы объявляем переменную themeMode, которая отображает либо светлую, либо тёмную тему, в зависимости от состояния режима theme в данный момент.
Теперь нашему компоненту-оболочке ThemeProvider назначена только что созданная переменная themeMode для пропса theme.
И, наконец, вместо обычной кнопки передаём компонент Toggle.
Помните, что в нашем компоненте Toggle мы определили и стилизовали кнопку, а так же передали им theme и toggleTheme в качестве пропсов. Итак, всё, что нам нужно сделать, это передать эти пропсы соответствующим образом компоненту Toggle, который будет выполнять функцию нашей кнопки в App.js.
Ура! Наша ночная тема установлена и умеет не менять цвет, когда страница обновляется или открывается в новой вкладке.
Давайте посмотрим на текущий результат в действии:
Почти всё работает хорошо, но есть одна маленькая вещь, сделав которую, переключение тем станет великолепным. Переключитесь на ночную тему и обновите страницу. Видите, что синий цвет в кнопке загружается чуть раньше, чем серый? Это происходит потому, что наш хук useState изначально запускает тему light. После этого запускается useEffect, проверяет localStorage и только потом устанавливает тёмную тему. Давайте перейдем к нашему пользовательскому хуку 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, mountedComponent ]
};
Выделенный код — всё, что нужно было добавить в useDarkMode.js. Мы создали другое состояние с именем mountComponent и установили значение по умолчанию в false, используя хук useState. Далее в хуке useEffect мы устанавливаем состояние mountComponent в значение true, используя setMountingComponent. Наконец, в возвращаемый массив добавляем состояние mountComponent.
А теперь давайте добавим немного кода в 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
return (
<>
{
videos.map((list, index) => {
return (
);
})}
>
);
};
export default App;
Мы добавили наше состояние mountComponent в качестве опоры для хука useDarkModeи проверили, установлен ли компонент, что и происходит в хуке useEffect. Если этого не произошло, то мы сделаем div пустым.
Давайте посмотрим на нашу тёмную веб-страницу.
Теперь, когда страница обновляется, цвет кнопки остаётся неизменным.
Заключение Ночная тема всё чаще становится фаворитом среди пользователей, а реализовать её в веб-приложении React становится значительно проще, если использовать тематическую оболочку ThemeProvider в styled-components. Двигайтесь вперёд и экспериментируйте со стилевыми компонентами при реализации тёмной темы. Кстати, вместо кнопки можно сделать значки.
Оригинал статьи