Что нужно знать о Redux - action/dispatch/reducer/store

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

В этой статье рассмотрим работу с Redux - его основные понятия, и как Redux работает с данными.

Redux - это стэйт менеджер для JS приложений. Он может работать не только в React приложениях. Просто так сложилось исторически, что они часто упоминаются вместе. Redux хранит состояние в дереве объектов внутри единого стора. Единственная возможность изменить состояние - отправить action. Action это объект, который описывает действие. Он как бы отвечает на вопрос "Что я хочу изменить в состоянии?".
// types.js
const ADD_TODO = "ADD_TODO"
// actions.js
import { ADD_TODO } from "./types.js"

export const addTodoAction = {
  type: ADD_TODO,
  payload: {
     id: 1,
     text: "Изучить Redux",
     done: false
   }
}
Далее action попадает в reducer, где описано как состояние должно быть изменено ("Как я хочу изменить состояние?").
//  todo-reducer.js
import { ADD_TODO } from "./types.js"

export const todoReducer = (state = [], action) => {
  switch (action.type) {
    case ADD_TODO: {
      const item = action.payload
      return [...state, item]
    }
    default:
      return state
  }
}

Для чего нужен Redux?

Redux и в целом любая другая система работы с состоянием нужна для контроля над состоянием. Веб приложения усложняются, добавляются новые фичи и контроль над приложением теряется. Становится сложно охватывать большой проект как единое целое. Становится cложно передавать изменения. Встроенными способами в React их приходилось бы передавать по цепочке от одного компонента в другой или через Context API. По дороге обновленное состояние может также влиять на другие компоненты и т.д. И такая бесконтрольность расползается по всему проекту и может послужить появлению новых багов и добавит сложность в отладке. Теряется прозрачность того что происходит.
Action’ы добавляют порядок в происходящие изменения. Action - это объект, и его можно залогировать и последовательно отслеживать как меняется состояние. Подытожив, можно вывести три принципа Redux:
  • Единственный источник правды - единый стор, который меняются и всегда содержит актуальные данные.
  • Состояние - только для чтения. Нельзя менять его напрямую - только через action’ы.
  • Изменения делаются только при помощи чистых функций.
  • Эти функции (редьюсеры) принимают в качестве аргумента старое состояние и возвращают обновленное состояние. И всегда при одних и тех же данных результат этих функций будет одинаков.

Понятия в Redux

Action

Action (экшн) - это источник данных в стор, Action включает тип и некоторую информацию (payload). Тип обычно имеет строковое значение. Он нужен чтобы reducer понимал какой action был отправлен. Далее к стору будет применен payload.

Reducer

Reducer (редьюсер) - это функция, которая определяет как должно меняться состояние в зависимости от аction’а. Редьюсеры должны быть чистым функциями - они должны вычислить следующее состояние и вернуть новый объект состояния. Никаких сайд эффектов, мутаций состояния, и вызовов API в редьюсере быть не должно.

Store

Store - объединяет аction’ы и редьюсеры
  • Хранит состояние приложения.
  • Предоставляет доступ к состоянию через getState.
  • Позволяет изменять состояние через dispatch.
  • Регистрирует подписчиков через subscribe.
Теперь когда мы знаем основные понятия сформулируем флоу - движение данных в Redux. Сначала вызываем функцию dispatch, передав в нее action.
// App.jsx
import { useDispatch } from "react-redux"
import { ADD_TODO } from "./types.js"

export default App() {
  const dispatch = useDispatch()
 
  const handleTodoAdd = () => {
    dispatch({
      type: ADD_TODO,
       payload: {
         id: 1,
         text: "Изучить Redux",
         done: false
       },
    })
  }

  return (
    <button type="button" onClick={handleTodoAdd}>Добавить todo</button>
  )
}
Далее Redux вызывает переданный в него редьюсер. В редьюсер отправляется два аргумента - текущее состояние (state) и action.
//  todo-reducer.js
import { ADD_TODO } from "./types.js"

export const todoReducer = (state = [], action) => {
  switch (action.type) {
    case ADD_TODO: {
      const item = action.payload
      return [...state, item]
    }
    default:
      return state
  }
}
Redux сохраняет весь объект, который вернулся из корневого редьюсера. Почему редьюсер - корневой? В Redux передается один редьюсер, но его можно разделить на несколько и собрать вместе при помощи combineReducers. Результатом этой функции будет корневой редьюсер. Обычно это делается, когда в приложении есть несколько модулей и есть смысл разделить большой редьюсер на несколько маленьких. Но для мелких приложений это совсем не обязательно.
// reducers.js
import { combineReducers } from "redux"
import { todoReducer } from "./todo-reducer"

export const rootReducer = combineReducers({
  todos: todoReducer,
})
Как уже упоминалось, Redux работает не только с ReactJS. Это универсальный инструмент. Однако для того чтобы использовать его в ReactJS нужен специальный байндинг, который ставится как отдельный пакет - react-redux.

Как подключить Redux к ReactJS проекту

Установить Redux с помощью команды.
npm i redux react-redux
Проинициализировать store и обернуть приложение тегом <Provider> c переданным в него объектом store.
// index.js
import React from "react"
import ReactDOM from "react-dom/client"

import { createStore } from "redux"
import { Provider } from "react-redux"

import App from "./App"
import { rootReducer } from "./reducers"

const store = createStore(rootReducer)

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

Как подключить шрифт в React

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

Рассмотрим три быстрых способах подключения шрифтов в React приложение.

font В HTML используется для указания начертания, размера шрифта, типографики текста. Вы можете добавить шрифты в свое React приложение разными способами.

Способ 1. С использованием link

Мы можем ссылаться на любые шрифты из Интернета, с помощью тега <link> внутри HTML-файла. Рассмотрим пример применения Google Fonts шрифта с помощью тега <link>.
  1. Переходим в fonts.google.com
  2. Кликаем на шрифт
Реакт добавить шрифт
  1. Кликаем на “Select Regular 400” (или на любые другие выбранные виды, можно несколько)
Реакт добавить шрифт
  1. Нажимаем на иконку “View selected families” сверху справа.
Реакт добавить шрифт
  1. В появившемся боковом меню переходим в секцию “Use on the web” и копируем код, расположенный под <link>
Реакт добавить шрифт
  1. Переходим в index.html. Если ваше приложение создано с помощью create-react-app, то index.html находится в папке public. Вставляем код, скопированный в п.5 внутрь <head>.
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
  1. На сайте Google Fonts в том же боковом меню находим секцию “CSS rules to specify families” и копируем код.
Реакт добавить шрифт
  1. Переходим в CSS файл и добавляем следующий стиль, вставляем скопированный код:
.font-roboto {
  font-family: 'Roboto', sans-serif; /* скопированный код */
}
  1. Применяем этот стиль в любом React компоненте
// App.jsx

import './App.css';

function App() {
  return (
    <div className="font-roboto">
      <p>Hello</p>
    </div>
  );
}

export default App;

Способ 2. С использованием Web Font Loader

Web Font Loader помогает загружать шрифты из Google Fonts, Typekit, Fonts.com и Fontdeck, а также самостоятельно размещенные веб-шрифты. Он разработан совместно компаниями Google и Typekit. Давайте посмотрим, как загрузить несколько шрифтов из Google Fonts и использовать их в компоненте React.
  1. Установим webfontloader
yarn add webfontloader
или
npm i webfontloader
  1. Импортируем webloader в компонент
import WebFont from 'webfontloader';
  1. Загружаем нужные шрифты, используя имя шрифта. Лучше использовать хук useEffect и позволить ему выполняться один раз при загрузке компонента. Поскольку шрифты должны быть загружены только один раз в приложении, вы можете загрузить их в файле index.js.
useEffect(() => {
  WebFont.load({
    google: {
      families: ['Droid Sans', 'Chilanka']
    }
  });
 }, []);
Здесь мы загружаем шрифты 'Droid Sans' и 'Chilanka'.
  1. Теперь вы можете использовать эти шрифты в компоненте React с помощью атрибута className или style. Чтобы использовать атрибут className, создайте CSS-класс в файле .css.
.font-loader {
  font-family: 'Chilanka';
}
Затем, в методе render() компонента добавьте следующее.
<div className="font-loader">
    Hello, World!
</div>
Или, с атрибутом style
<div style={{fontFamily: 'Droid Sans'}}>
   Hello, World!
</div>

Способ 3. С использованием @font-face

Иногда шрифты должны быть загружены локально и упакованы в приложение. @font-face - это правило CSS для определения имени шрифта путем указания на него с помощью URL.
  1. Создадим папку fonts в каталоге src.
  2. Загрузим необходимые шрифты. В этом примере мы загрузим шрифт Lobster. Для этого выберем шрифт в Google Fonts, нажмем “Download family” и распакуем архив.
Реакт добавить шрифт
  1. Скопируем содержимое в папку src\fonts.
Реакт добавить шрифт
  1. Затем импортируем шрифты в файл index.js.
import './fonts/Lobster/Lobster-Regular.ttf';
  1. В файл index.css добавим
@font-face {
font-family: "LobsterRegular";
src: local("LobsterRegular"),
 url("./fonts/Lobster/Lobster-Regular.ttf") format("truetype");
font-weight: normal;
}
  1. Теперь добавим имя класса в файл App.css, который использует этот шрифт.
.font-lobster {
 font-family: "LobsterRegular";
}
  1. Применяем этот стиль в любом React компоненте
// App.jsx

import './App.css';

function App() {
  return (
    <div className="font-lobster">
      <p>Hello</p>
    </div>
  );
}

export default App;