В этой статье объясняется, как использовать промежуточное ПО в Redux. Это вторая часть серии из пяти статей о Redux:

  1. Редукс,
  2. Промежуточное ПО Redux (эта статья),
  3. Реагировать Редукс,
  4. Набор инструментов RTK Redux,
  5. РТК запрос.

К концу вы узнаете:

  • что такое промежуточное ПО и как оно работает в Redux,
  • как выполнять асинхронные операции в Redux благодаря промежуточному ПО,
  • как использовать Redux Thunk.

TL;DR — вы можете увидеть промежуточное ПО и преобразователь в действии в моем CodeSanbox.

Что такое ПО промежуточного слоя

Промежуточное ПО находится между методом отправки и редьюсерами.

Возвращаясь к метафоре из предыдущей статьи, в которой Redux сравнивается с кофейней:

Клиенты (создатели действий) кофейни делают заказ (действие), который принимает официант (диспетчер) и приносит его в менеджер (редуктор). Промежуточное ПО было бы обходным путем, по которому официант мог бы пройти, прежде чем добраться до менеджера.

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

Зачем нам промежуточное ПО

Промежуточное программное обеспечение делает то, что редюсеры не могут.

Редьюсеры должны быть чистыми функциями и не могут создавать побочные эффекты, такие как:

  • запись значения в консоль,
  • сохранение файла,
  • использование асинхронных операций,
  • создание уникальных случайных значений (например, Math.random() или Date.now())

Промежуточное ПО позволяет использовать логику с побочными эффектами.

Как выглядит промежуточное ПО

Вот минималистичный пример того, как создать промежуточное ПО и как подключить его к store.

import { createStore, applyMiddleware } from "redux";

const customMiddleware = (store) => (next) => (action) => {
  // ... some logic
};

const store = createStore(
  reducer,
  defaultState, // optional
  applyMiddleware(customMiddleware) // optional
);

Вы заметили странное обозначение (store) =› (next) =› (action)?

Промежуточное ПО получает сохранение, затем возвращает функцию, которая получает функцию следующий, и возвращает другую функцию, которая получает действие.

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

Что такое каррированная функция

Каррирование просто означает вычисление функций с несколькими аргументами и их разложение на последовательность функций с одним аргументом. Этот пример говорит сам за себя:

// Noncurried version
const add = (a, b, c) => {
  return a + b + c;
};
console.log(add(2, 3, 5)); // 10

// Curried version
const curriedAdd = (a) => {
  return (b) => {
    return (c) => {
      return a + b + c;
    };
  };
};
console.log(curriedAdd(2)(3)(5)); // 10

Минималистическое приложение Redux с промежуточным ПО

Вернемся к минималистичному примеру Redux-приложения из предыдущей статьи. Мы добавим промежуточное ПО, которое использует console.log() для информирования нас об изменении состояния.

import { createStore, applyMiddleware } from "redux";

const reducer = (state = 0, action) => {
  switch (action.type) {
    case "ADD":
      return state + 1;
    default:
      return state;
  }
};

export const customMiddleware = (store) => (next) => (action) => {
  console.log("default state:", store.getState());
  next(action);
  console.log("updated state:", store.getState());
  return;
};

const store = createStore(reducer, applyMiddleware(customMiddleware));

store.dispatch({ type: "ADD" });

Асинхронное ПО промежуточного слоя

Теперь давайте воспользуемся промежуточным ПО для асинхронного вызова.

Обратите внимание, как промежуточное ПО отправляет данные, полученные с сервера.

const serverRequest = () => {
  // Simulating a promise from a server
  const result = 1;
  return new Promise((resolve) => setTimeout(() => resolve(result), 2000));
};

const customMiddleware = (store) => (next) => (action) => {
  switch (action.type) {
    case "SERVER":
      console.log(store.getState());
      serverRequest().then((response) => {
        store.dispatch({ type: "ADD", payload: response });
        console.log(store.getState());
      });
      break;
    default:
      next(action);
  }
};

Это промежуточное ПО начинает слишком походить на редюсер. Более чистым подходом было бы написать эту асинхронную логику в Action Creator. Но Redux ожидает, что dispatch получит простой объект, а не асинхронную функцию. Поэтому мы перепишем промежуточное ПО, чтобы оно распознавало, когда мы передаем простой объект действия и когда мы передаем настоящую асинхронную функцию.

Таким образом, мы можем извлечь асинхронную логику в Action Creator.

export const customMiddleware = (store) => (next) => (action) => {
  // If the "action" is actually a function instead...
  if (typeof action === "function") {
    // then call the function and pass on dispatch and getState
    return action(store.dispatch, store.getState);
  }

  // Otherwise, it's a normal action - send it onwards
  return next(action);
};

// Possibility 1: normal action
const normalAction = () => ({ type: "ADD", payload: 1 });
store.dispatch(normalAction());

// Possibility 2: asynchronious action
const asyncFunctionAction = () => (dispatch, getState) => {
  console.log("current state", getState());
  serverRequest().then((response) => {
    dispatch({ type: "ADD", payload: response });
    console.log("updatedstate", getState());
  });
};
store.dispatch(asyncFunctionAction());

Редукс Преобразователь

То, что мы создали на предыдущем шаге, на самом деле является простой версией Redux Thunk.

Для простоты мы можем использовать готовый пакет redux-thunk.

import { createStore, applyMiddleware } from "redux";
import thunkMiddleware from "redux-thunk";
import { customMiddleware } from "./custom-middleware";

const store = createStore(
  reducer,
  applyMiddleware(customMiddleware, thunkMiddleware)
);

Заключение

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

Мы продолжим работу с Redux Toolkit (RTK) в следующей статье.

Если эта статья оказалась для вас полезной, нажмите кнопку хлопать в ладоши 👏. Кроме того, не стесняйтесь комментировать! Буду рад помочь :)