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

Наш стек Redux

Для начала у нас есть простое приложение React Redux, которое позволит нам добавить новое такси или сбросить его.

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

Во-первых, давайте посмотрим на магазин и где он используется:

import {
  createStore
} from “redux”;
import {
  reducers
} from “./Reducers”;
import {
  Provider
} from "react-redux";
import App from "./App";
export function configureStore(initialState = {}) {
  const store = createStore(reducers, initialState);
  return store;
}
export const store = configureStore();

Затем мы традиционно импортируем это в наш провайдер точки входа, который обертывает компонент приложения:

import { store } from "./Store";
<Provider store={store}>
  <App />
</Provider>

Это должно выглядеть достаточно знакомо для нашего экземпляра Store — теперь давайте посмотрим на наш редюсер:

import {
  combineReducers
} from "redux";
export const taxis = (state = {}, action) => {
  switch (action.type) {
    case RANDOM_TAXI:
      return action.taxi;
    case RESET_TAXI:
      return {};
    default:
      return state;
  }
};
export const reducers = combineReducers({
  taxis
});

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

Теперь мы рассмотрим, как мы можем удалить наши зависимости Redux и React Redux и оставить только React, управляющий состоянием нашего приложения.

Хук useReducer

React Hooks были представлены в версии 16.8, и они представляют собой мощный набор функций, которые устраняют разрыв между классическим и функциональным.

Функции, которые позволяют подключаться к функциям состояния и жизненного цикла React из функциональных компонентов.

Мы рассмотрим хук useReducer и то, как мы можем использовать его для постепенного отказа от наших старых редьюсеров/промежуточного программного обеспечения и т. д., которые мы написали для нашего устаревшего приложения Redux. Хук useReducer — это сложный инструмент для обработки сложного состояния, он предназначен для использования в качестве усовершенствования хука useState.

Мы собираемся сохранить нашу неизменность с помощью Immer. Документы Immer дают хорошее объяснение того, что он делает:

Использование Immer похоже на личного помощника; он берет письмо (текущее состояние) и дает вам копию (черновик), чтобы записать изменения. Как только вы закончите, помощник возьмет ваш черновик и создаст для вас настоящую неизменную окончательную букву (следующее состояние).

Учитывая это, вот как выглядит наш новый редуктор:

export const reducer = produce((draft, action) => {
  switch (action.type) {
    case SWITCH_TAXI:
      draft.taxi = action.taxi;
      break;
    case RESET_TAXI:
      draft.taxi = {}
      break;
  }
});

Вернемся к нашему Магазину, мы собираемся внести здесь несколько изменений, и здесь в игру вступает наш хук useReducer, useReducer возвращает текущее состояние в паре с методом отправки — мы знакомы с этой концепцией из-за нашего опыта работы с приложением Redux.

// Store
export const AppStore = createContext();
// Providers
export function AppStoreProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, {
    taxi: {}
  });
  return (
    <AppStore.Provider
      value = {
        {
          state,
          dispatch
        }
      }
    >
      { children }
    </AppStore.Provider>
  );
}

Мы просто собираемся передать эту пару состояние, отправка вниз по дереву провайдеров, чтобы наши компоненты могли получить наше новое состояние.

// App.js
const { state, dispatch } = useContext(AppStore);
const { taxi } = state;

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

Наши действия — это просто объекты JavaScript, поэтому их можно перенести в наш новый стек, но нам нужно внести небольшие коррективы в то, как мы их отправляем.

Раньше в нашем стеке Redux мы сопоставляли эти действия как свойства, используя mapDispatchToProps в нашем AppContainer.js, но теперь мы можем просто обернуть функцию в полученную функцию отправки. из useContext нашего Магазина.

<button
  type="button"
  className="btn btn-primary"
  onClick={() =>
    dispatch(
      randomTaxi({
        car: chance.word(),
        driver: chance.name(),
        dispatched: chance.bool()
      })
    )
  }
>
  Random Taxi
</button>

Теперь у нас есть приложение React без Reduxless, которое по-прежнему имеет масштабируемое управление состоянием без особых усилий. В конечном итоге это позволило нам создать более оптимизированную кодовую базу.

Полное приложение доступно в Code Sandbox, поэтому вы можете увидеть все аспекты стека, устаревшую версию и версию useReducer.