Кастомный React хук для изменения темы веб-приложения

2 года назад·2 мин. на чтение

Пишем кастомный React хук useTheme, который динамически меняет тему веб-приложения с помощью CSS переменных.

В этой статье напишем кастомный хук на ReactJS, который будет менять тему веб-приложения. Особенности хука useTheme, который мы реализуем:
  • использует CSS переменные
  • быстро кастомизируется
  • плавный переход между темами
  • умеет сохранять выбранную тему в local storage

Код хука useTheme

Сам хук выглядит довольно просто.
// hooks/use-theme.js

import { useLayoutEffect, useState } from 'react'

const isDarkTheme = window?.matchMedia('(prefers-color-scheme: dark)').matches
const defaultTheme = isDarkTheme ? 'dark' : 'light'

export const useTheme = () => {
  const [theme, setTheme] = useState(
    localStorage.getItem('app-theme') || defaultTheme
  )

  useLayoutEffect(() => {
    document.documentElement.setAttribute('data-theme', theme)
    localStorage.setItem('app-theme', theme)
  }, [theme])

  return { theme, setTheme }
}
prefers-color-scheme используется для определения выбранной пользователем темы (светлая или темная). Пользователь указывает свои предпочтения через настройку операционной системы или через настройку user agent. document.documentElement.setAttribute('data-theme', theme) - этой строчкой мы добавляем кастомный data-атрибут в тег html. Таким образом, например, при выборе светлой темы в теге html появится data-атрибут data-theme="light".

Добавление CSS переменных

Далее мы должны указать CSS переменные, которые будут иметь различные значение для разных тем. Например, --button-text-color имеет значение #ffffff при темной теме и #252525 - при светлой.
/* index.css */

html[data-theme='dark'] {
  --button-text-color: #ffffff;
  --button-background-color: #4e005c;
  --button-border-color: #ba8fc2;

  --background-color: #292929;
  --icon-color: #ba8fc2;
}

html[data-theme='light'] {
  --button-text-color: #252525;
  --button-background-color: #f9d4ff;
  --button-border-color: #4e005c;

  --background-color: #dfdfdf;
  --icon-color: #4e005c;
}

Применение CSS переменных

Далее нам нужно применить эти CSS переменные к соответствующим элементам. Добавление transition дает плавность при переключении тем.
/* App.css */

.app__container {
  background-color: var(--background-color);
  transition: background-color 200ms linear;
}

.app__logo {
  color: var(--icon-color);
  transition: color 500ms linear;
}

.app__button,
.app__button:hover,
.app__button:focus,
.app__button:active,
.app__button:not(:disabled):not(.disabled):active {
  color: var(--button-text-color);
  background-color: var(--button-background-color);
  border-color: var(--button-border-color);
  transition: color 500ms linear, background-color 500ms linear,
    border-color 500ms linear;
}

Использование хука useTheme

Здесь добавлены две кнопки, каждая из которых отвечает за переключение на светлую или темную тему. Они обернуты в ButtonGroup. Элементы логотип, контейнер и кнопка имеют соответствующие CSS классы, которые мы описали выше - app__logo, app__container и app__button. В этих классах были использованы CSS переменные.
import React from 'react'

import Button from 'react-bootstrap/Button'
import ButtonGroup from 'react-bootstrap/ButtonGroup'

import { FaCoffee } from 'react-icons/fa'
import { useTheme } from './hooks/use-theme'

import './App.css'

export default function App() {
  const { theme, setTheme } = useTheme()

  const handleLightThemeClick = () => {
    setTheme('light')
  }
  const handleDarkThemeClick = () => {
    setTheme('dark')
  }

  return (
    <div className="app__container w-100 h-100 d-flex flex-column">
      
      <div className="p-3 d-flex justify-content-end">
        <ButtonGroup aria-label="Theme toggle">
          <Button variant="secondary" onClick={handleLightThemeClick}>
            Light
          </Button>
          <Button variant="secondary" onClick={handleDarkThemeClick}>
            Dark
          </Button>
        </ButtonGroup>
      </div>

      <div className="flex-grow-1 d-flex flex-column justify-content-center align-items-center">
        <FaCoffee size={100} className="app__logo mb-5" />
        <div className="d-flex">
          <Button className="app__button" type="button">
            Subscribe
          </Button>
        </div>
      </div>
    </div>
  )
}
Исходный код

Создание реактивных форм в React с помощью Formik

месяц назад·1 мин. на чтение

В этой статье мы покажем, как использовать библиотеку Formik для создания реактивных форм в React.

Formik - это библиотека для управления формами в React. Она предоставляет мощные инструменты для обработки ввода данных, валидации и отправки формы на сервер. В этой статье мы рассмотрим, как использовать Formik для создания реактивных форм в функциональных компонентах React.

Установка Formik

Прежде чем начать, нам нужно установить Formik. Вы можете сделать это, выполнив следующую команду:
npm install formik

Импорт Formik и необходимых компонентов

После установки Formik мы можем импортировать его и необходимые компоненты в нашем функциональном компоненте:
import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";

Создание формы

Для создания формы мы будем использовать компонент Formik. Он принимает объект с начальными значениями полей формы, функцию обработчика отправки формы и другие необязательные свойства.
const MyForm = () => {
  return (
    <Formik
      initialValues={{ name: "", email: "" }}
      onSubmit={(values) => {
        // Обработка отправки формы
        console.log(values);
      }}
    >
      <Form>
        {/* Поля формы */}
      </Form>
    </Formik>
  );
};

Добавление полей формы

Для добавления полей формы мы используем компонент Field. Он принимает имя поля, тип и другие свойства. Мы также можем использовать компонент ErrorMessage для отображения сообщений об ошибках.
const MyForm = () => {
  return (
    <Formik
      initialValues={{ name: "", email: "" }}
      onSubmit={(values) => {
        // Обработка отправки формы
        console.log(values);
      }}
    >
      <Form>
        <div>
          <label htmlFor="name">Имя:</label>
          <Field type="text" id="name" name="name" />
          <ErrorMessage name="name" component="div" />
        </div>

        <div>
          <label htmlFor="email">Email:</label>
          <Field type="email" id="email" name="email" />
          <ErrorMessage name="email" component="div" />
        </div>

        <button type="submit">Отправить</button>
      </Form>
    </Formik>
  );
};

Валидация формы

Formik также предоставляет удобные средства для валидации формы. Мы можем определить функцию валидации и передать ее в качестве свойства validate в компонент Formik.
const validateForm = (values) => {
  const errors = {};

  if (!values.name) {
    errors.name = "Поле Имя обязательно для заполнения";
  }

  if (!values.email) {
    errors.email = "Поле Email обязательно для заполнения";
  }

  return errors;
};

const MyForm = () => {
  return (
    <Formik
      initialValues={{ name: "", email: "" }}
      validate={validateForm}
      onSubmit={(values) => {
        // Обработка отправки формы
        console.log(values);
      }}
    >
      {/* ... */}
    </Formik>
  );
};

Обработка отправки формы

Для обработки отправки формы мы передаем функцию обработчика в свойство onSubmit компонента Formik. В этой функции мы можем выполнять любую логику, связанную с отправкой данных на сервер.
const MyForm = () => {
  return (
    <Formik
      initialValues={{ name: "", email: "" }}
      validate={validateForm}
      onSubmit={(values, { setSubmitting }) => {
        setTimeout(() => {
          // Симуляция отправки формы
          console.log(values);
          setSubmitting(false);
        }, 2000);
      }}
    >
      {/* ... */}
    </Formik>
  );
};

Полезные свойства и методы Formik

Formik предоставляет множество полезных свойств и методов для управления формой. Некоторые из них:
  • values - объект, содержащий значения полей формы
  • errors - объект, содержащий ошибки валидации
  • touched - объект, содержащий информацию о том, каких полей "коснулся" пользователь
  • handleChange - функция для обновления значения поля при его изменении
  • handleBlur - функция для отслеживания "прикосновений" к полю
  • isSubmitting - флаг, указывающий, отправляется ли форма в данный момент
  • isValidating - флаг, указывающий, происходит ли валидация формы в данный момент
  • resetForm - метод для сброса значений полей формы
Это только некоторые из возможностей Formik. Вы можете ознакомиться с полной документацией на официальном сайте, чтобы узнать больше.

Итоги

Formik - это мощная и гибкая библиотека для создания реактивных форм в React. Она предоставляет все необходимые инструменты для управления формами, валидации и отправки данных. В этой статье мы рассмотрели основы использования Formik в функциональных компонентах React. Теперь вы можете применить эти знания в своих проектах и создавать интерактивные и удобные формы.