Translate

Поиск по этому блогу

Показаны сообщения с ярлыком Context API. Показать все сообщения
Показаны сообщения с ярлыком Context API. Показать все сообщения

воскресенье, 5 июля 2020 г.

Следует ли использовать React Context поверх Redux в приложении React?







Redux - это библиотека, которая помогает управлять состоянием вашего приложения.

Redux следует использовать в средних и больших одностраничных приложениях со сложным потоком данных.

Redux может добавить ненужную сложность вашему приложению, если оно действительно маленькое с простым потоком данных.

С другой стороны, в версии 16.3 React команда представила Context, который также помогает управлять состоянием React - приложения.

Зачем нам нужен инструмент управления состоянием - state

Если у нас есть несколько компонентов, которые совместно используют один и тот же фрагмент данных, и у них нет родительских и дочерних отношений, то для решения проблемы используют Redux. В Redux у нас есть один фрагмент идентичных данных (identical data), который синхронизирован со всеми компонентами, которые используют этот фрагмент данных.

Одним из решений может быть использование event raising в Angular и использование props в React. Позже в большом коде это решение превратится в спагетти событий ( event spaghetti) и в React - приложении, мы будем сталкиваться с проблемой получения (доступности) props - так называемым - prop drilling.

Со всей базы кода мы начнем получать события, и будет сложно отслеживать их все. Таким образом, поток данных становится непредсказуемым. Facebook столкнулся с этой проблемой еще в 2014 году, поэтому они представили Flux Architecture.

Redux - это упрощенная и легкая реализация Flux Architecture.

Основные преимущества Redux

  • Отделенная, расцепленная архитектура
  • Тестируемость
  • Отличная поддержка инструментов в Chrome, Firefox
  • Простая реализация функций отмены / повтора - выкл./вкл.


Когда использовать Redux

В следующих случаях мы можем использовать redux:
  • Один и тот же фрагмент данных в нескольких местах
  • Несколько (views) - представлений, которые должны работать с теми же данными в синхронизации
  • Данные могут быть изменены несколькими действиями / несколькими пользователями


Прежде чем мы получим context, для решения проблемы использовали redux, такой как спагетти - event spaghetti и prop drilling. Redux приносит много сложности для простых приложений. Позже React - команда представила контекстную концепцию для простоты.

С помощью контекста - context мы можем передавать данные по нашему дереву компонентов без prop drilling. Таким образом, в одном компоненте мы предоставим некоторый контекст или общие данные, а в другом компоненте мы будем использовать данные.

Теперь мы увидим, как мы можем использовать контекст поддерживаемым и инкапсулированным образом с помощью typescript и React - хуков. В нашем примере мы будем использовать понятия действий - actions, редуктора - reducer и состояния - state из redux. Итак, сначала мы рассмотрим основные концепции Redux.

Основная концепция Redux

  • Хранилище - Store - один объект JS, который содержит состояние приложения.
  • Действия - Actions - Обычный объект JS, представляющий, что что-то произошло. Подобные события.

  • {type: ‘USER_LOGGED_IN’}

  • Редуктор Reducer- функция, которая определяет, как состояние state изменяется в ответ на действие - action. Это не меняет состояние. Возвращает новое состояние.
В Redux мы не обновляем напрямую состояние. Сначала мы отправим действие (action). Оно попадет в хранилище (store). Хранилище (store) передает действие (action) корневому редуктор (reducer). На основе действия редуктор (reducer) возвращает новое состояние (state), и хранилище (store) обновляется внутри.


На картинке показано изменение состояния компонента при клике


Использование контекста в приложении React

Для демонстрации идея состоит в том, чтобы создать компонент ,mark>Header, который будет отвечать за отображение кнопки «Вход» или «Выход из системы» в зависимости от состояния из хранилища - store. Мы отправим действие action LOGIN и LOGOUT из компонента Header.

Мы сделаем все 5 простых шагов.
  1. Create Actions - Создать действия
  2. Create State - Создадим состояние
  3. Create Reducer - Создадим редуктор
  4. Create Context - Создадим контекст
  5. Create Header component - Создадим сам компонент Header

1 - Создание действий - Actions

Сначала мы должны определить некоторые действия, которые будут отвечать за изменение состояния нашего приложения. У нас есть два действия "Войти" и "Выйти".

Action.ts
export enum Action {
  LOGIN = "LOGIN",
  LOGOUT = "LOGOUT"
}


Создание состояния - State

Теперь мы должны определить, какие данные мы хотим сохранить в нашем хранилище контекста. Поэтому мы создадим интерфейс для этого, а также начальное значение для состояния. В нашем случае мы просто сохраним имя пользователя.

state.ts
export interface StateType {
  username: string | null;
}
export const initialState: StateType = {
  username: null
};


Создание редуктора - Reducer

Pure Function или Чистая функция - это такая функция, которая при получении однинх и тех же данных на входе, всегда получаем один и тот же выход из функции, то функция является чистой функцией. Так что побочных эффектов не будет. Это дает нам легкую тестируемость и простой способ реализации функций отмены / возврата.
Если вы забыли некоторые возможности JavaScript, то я рекомендую вам посмотреть статьи в моем блоге - ES6 и JS
Далее мы создадим редуктор. Редуктор это чистая функция. Это функция с двумя аргументами. Первый - это состояние по умолчанию с типом StateType, а второй - объект с props- свойствами type и полезной нагрузки - payload. Свойство type определяет тип действия, а payload содержит дополнительные данные от диспетчера.

Reducer.ts
import { StateType, initialState } from "./state";
import { Action } from "./Action";
export type ActionType = {
  type: Action;
  payload?: StateType;
};
const reducer = (state: StateType, { type, payload }: ActionType) => {
switch (type) {
    case Action.LOGIN:
      return { ...state, ...payload };
  
    case Action.LOGOUT:
      return initialState;
default:
      return state;
  }
};
export default reducer;


Для action -действия LOGIN мы передадим имя пользователя в качестве полезной нагрузки - payload, когда мы отправим действие - action. Для выхода из системы нам не нужны никакие данные в качестве полезной нагрузки -payload. Мы просто сбросим состояние до начального значения state.

4 - Создадим контекст -Context

Теперь мы создадим контекст. Сначала мы определим ContextType, а затем создадим Store. После этого мы создадим StoreProvider.

Context.tsx
import React, { createContext, Dispatch, useReducer } from "react";
import reducer, { ActionType } from "./Reducer";
import { StateType, initialState } from "./state";
export interface ContextType {
  state: StateType;
  dispatch: Dispatch<ActionType>;
}
export const RootStore = createContext({} as ContextType);
export const RootStoreProvider: React.FC = ({ children }) => {
const [state, dispatch] = useReducer<React.Reducer<StateType, ActionType>>(reducer, initialState);
const value = { state, dispatch };
return <RootStore.Provider value={value}>{children}</RootStore.Provider>;
};


Теперь мы обернем наш корневой компонент этим RootStoreProvider для доступа к хранилищу - store глобально.

index.tsx
const App = () => {
  return <RootStoreProvider>....</RootStoreProvider>;
};



Теперь у нас все готово. Мы создадим простой компонент Header, из которого мы будем использовать RootStore для получения информации о пользователе, а также для обновления информации.

5 - Создадим сам компонент Header



Header/index.tsx
import React from "react";
const Header = () => {
  const username = null;
  const toggleLoginLogoutHandler = () => {};
  return (
    <div
      style={{
        backgroundColor: "#dee5ec",
        padding: 10,
        display: "flex",
        justifyContent: "space-between",
      }}
    >
      <div>
        <p>Header</p>
        {username && <p>Logged in as {username}</p>}
      </div>
      <button onClick={toggleLoginLogoutHandler}>
        {username ? "Logout" : "Login"}
      </button>
    </div>
  );
};
export default Header;



Теперь мы будем использовать наш RootStore для получения имени пользователя, а также для отправки действий по входу и выходу из системы.

import React, { useContext } from "react";
import { RootStore } from "../../Context";
import { Action } from "../../Action";
const Header = () => {
  const {
    state: { username },
    dispatch
  } = useContext(RootStore);
const toggleLoginLogoutHandler = () => {
    if (username) {
      dispatch({ type: Action.LOGOUT });
    } else {
      dispatch({ type: Action.LOGIN, payload: { username: "Ashraful" } });
    }
  };
.......


Наконец, обновите корневой компонент.

.......
import Header from "./src/components/Header";
const App = () => {
  return (
    <RootStoreProvider>
      <Header />
    </RootStoreProvider>
  );
};


Вот и все. Мы внедрили React context со всеми концепциями Redux - компонентов. Вот ссылка на полный код.







Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook. Пишите мне - kolesnikovy70 почта gmail.com


Удачного кодирования!                                                                                                                                                              

Телеграм канал - Full Stack JavaScript Developer
Помочь проекту (любая валюта). DONATE


Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.Пишите мне - kolesnikovy70 почта gmail.com