Translate

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

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

воскресенье, 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

среда, 27 мая 2020 г.

React . Детальный обзор Context API

React Context API предоставляют механизм для передачи данных через дерево компонентов без передачи реквизитов на каждый уровень вручную.



Нам не нужно передавать данные на каждый уровень, вместо этого мы создаем контекст для компонента более высокого уровня и делаем данные контекста доступными для дочерних или дочерних компонентов. Статья посвящена подробному пониманию концепций React Context.

Друзья, я ранее уже много раз рассказывал вам об использовании реакт-хуков для быстрого, удобного и простого построения ваших приложений. Если вы что-то подзабыли, то welcome к моим постам по - React. Там вы найдете много простых и интересных решений, в частности - полноценное приложение построенное только на функциональных компонентах и реакт-хуках. Стоит посмотреть построение React-Сайта и некоторые мои статьи и переводы на gitHub Там я много пишу о самых распространенных и не очень решениях для построений реакт-приложений.


Когда нужно использовать React Context

В этом разделе мы увидим, когда нам нужно использовать React Context API. API React Context просты в использовании и очень эффективны. Приложение React может состоять из иерархии Компонентов, имеющих отношение Parent-Child, иерархия может иметь несколько уровней.

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



Пример кода:

import React from "react";

export default class GrandParents extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      surName: "Gupta",
    };
  }
  render() {
    return (
      <>
        <h1>This is the Grand Parent Component</h1>
        <Parents surName={this.state.surName} />
      </>
    );
  }
}
function Parents(props) {
  return (
    <div>
      <h2>This is a Parent Component</h2>
      <Children surName={props.surName} />
    </div>
  );
}
function Children(props) {
  return (
    <div>
      <b>Inherited Child Properties from Grand Parents: </b>
      <label>{props.surName}</label>
    </div>
  );
}



Code Sandbox link

В приведенном выше примере у нас есть компонент верхнего уровня «GrandParent», который содержит некоторую строку данных «surName». Этот компонент содержит другой компонент «Parent», которому мы отправляем эту информацию о «surName» в данных «props». Этот «родительский - Parent» компонент не использует эти данные, ему просто нужно передать эти данные следующему дочернему компоненту «Children», а затем этот компонент «Children» использует эту информацию «surName».

Проблема с вышеуказанным кодом

В приведенном выше сценарии данные требуются только на дочернем уровне. Но мы распространяем данные по всей Иерархии, чтобы сделать их доступными на более низком уровне («Children»). На первый взгляд ничего особенного, но представьте, что на месте одного родительского компонента, который просто передает пропсы ниже, не один а десять, а то и двадцать уровней компонентов? Используя контекст, мы можем сократить издержки на передачу данных по всей иерархии, чтобы сделать их доступными для компонентов самого низкого уровня. Далее давайте рассмотрим детали того, как уменьшить издержки на передачу props с помощью React Context.

Работа с контекстом React - React Context

Чтобы решить вышеуказанную проблему, мы можем использовать API React Context.React Context API состоит из:
  1. Context Object

    Чтобы использовать Context в приложении, нам сначала нужно создать объект контекста - Context Object, который способен хранить данные для приложения. Этот объект можно сделать доступным для иерархии для подачи или получения данных. Приведенный ниже код позволит нам создать объект контекста для всей иерархии.

    const FamilyContext = React.createContext({});


  2. Context Provider

    const FamilyProvider = FamilyContext.Provider;

    Context Provider используется для предоставления значения этому объекту контекста - Context Object. Мы можем добавить к контексту, используя этот объект Context Provider.


  3. Context Consumer

    const FamilyConsumer = FamilyContext.Consumer;

    Этот объект потребителя - Context Consumer используется для получения значения контекста и предоставления значения для компонента. Значение, сохраненное в контексте с использованием «Context Provider», может быть получено с помощью объекта Consumer Context.

    Давайте попробуем увидеть полностью реализованный код для того же сценария.


import React from "react";
const FamilyContext = React.createContext({});
export const FamilyProvider = FamilyContext.Provider;
export const FamilyConsumer = FamilyContext.Consumer;

export default class GrandParents extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      surName: "Kolesnikov",
      random: "Text",
    };
  }
  render() {
    return (
      <FamilyContext.Provider value={this.state}>
        <div>
          <h1>Grand Parents Initial Surname: </h1>
          {this.state.surName}
        </div>
        <Parents></Parents>
      </FamilyContext.Provider>
    );
  }
}

function Parents(props) {
  return (
    <div>
      <h2>Inherited Parent Property With "props":</h2>
      <label></label>
      <br />
      <br />
      <br />
      <Children />
    </div>
  );
}

class Children extends React.Component {
  render() {
    return (
      <FamilyConsumer>
        {(context) => {
          return (
            <div>
              <h3>Inherited Child Properties without "props": </h3>
              <label>{context.surName}</label>
            </div>
          );
        }}
      </FamilyConsumer>
    );
  }
}




Code Sandbox link

В приведенном выше коде следует обратить внимание на следующее:

  1. Сверху мы создали Объект контекста (FamilyContext), Объект провайдера (FamilyProvider) и Объект потребителя (FamilyConsumer).
  2. Значение добавляется в контекст в компоненте «GrandParent» с использованием объекта «Провайдер» - Provider Object. Мы используем свойство value для добавления необходимых данных в объект провайдера -Provider Object. Данные не нужно использовать в компоненте «Родитель» - Parent.
  3. Чтобы сделать данные напрямую доступными для компонента Children, мы используем Consumer Object - объект-потребитель (FamilyConsumer). Это сделает данные доступными непосредственно дочерним компонентам.
А если в двух словах, то мы просто взяли от реакта нужные нам части контекста и определили их в самом верху файла:

const FamilyContext = React.createContext({});
export const FamilyProvider = FamilyContext.Provider;
export const FamilyConsumer = FamilyContext.Consumer;


Затем, просто обернули все, что рендерим в корневом компоненте в провайдер, в который и передали пропсы, а в нужном месте (Дочерний компонент) достали их из контекста через функцию.

Все это в коде выделил красным!

PS

В приведенном выше примере данные не нужно передавать по всей иерархии, данные были доступны на верхнем уровне и были непосредственно доступны дочернему элементу с помощью объекта контекста. Отсюда устраняется необходимость передачи данных с использованием «реквизита - props» по всей иерархии.

Завтра, я более подробно расскажу вам об использовании этого удивительно полезного хука и познакомлю вас с некоторыми новыми аспектами его применения.

React. Как использовать «useContext» в React Hooks

Хотите оставаться в курсе новинок в области JS-программирования? Подписывайтесь на мой канал, вступайте в группу на Facebook. Если понравившаяся статья оказалась еще и полезной, то буду благодарен вашим пожертвованиям на развитие сайта - кнопка DONATE ниже (любая валюта).

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

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


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