В этой статье объясняется, как использовать промежуточное ПО в Redux. Это вторая часть серии из пяти статей о Redux:
- Редукс,
- Промежуточное ПО Redux (эта статья),
- Реагировать Редукс,
- Набор инструментов RTK Redux,
- РТК запрос.
К концу вы узнаете:
- что такое промежуточное ПО и как оно работает в 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) в следующей статье.
Если эта статья оказалась для вас полезной, нажмите кнопку хлопать в ладоши 👏. Кроме того, не стесняйтесь комментировать! Буду рад помочь :)