middleware react что это

Redux. Простой как грабли

Мне уже доводилось заглядывать в репозиторий библиотеки redux, но откуда-то появилась мысль углубиться в его реализацию. Своим в некотором роде шокирующим или даже разочаровывающим открытием я хотел бы поделиться с сообществом.

TL;DR: базовая логика redux помещается в 7 строк JS кода.

О redux вкратце (вольный перевод заголовка на гитхабе):

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

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

Я склонировал репозиторий redux, открыл в редакторе папку с исходниками (игнорируя docs, examples и прочее) и взялся за ножницы клавишу Delete:

… потому что мог. Ну или потому что поленился писать для них примеры. Но без корнер-кейсов они ещё менее интересны, чем то, что ждёт вас впереди.

А теперь давайте разберём то, что осталось

Пишем redux за 7 строк

Весь базовый функционал redux умещается в малюсенький файлик, ради которого вряд ли кто-нибудь будет создавать github репозиторий 🙂

Так устроен redux. 18 страниц вакансий на HeadHunter с поисковым запросом «redux» — люди, которые надеются, что вы разберетесь в 7 строках кода. Всё остальное — синтаксический сахар.

С этими 7 строками уже можно писать TodoApp. Или что угодно. Но мы быстренько перепишем TodoApp из документации к redux.

Уже на этом этапе я думал бросить микрофон со сцены и уйти, но show must go on.
Давайте посмотрим, как устроен метод.

combineReducers

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

Используется он так:

Дальше использовать этот store можно так же, как предыдущий.

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

В документации используют модный синтаксис из ES6 (7/8/∞):

и соответственно переименовывают todoReducer в todos и counterReducer в counter. И многие в своём коде делают то же самое. В итоге разницы нет, но для человека, знакомящегося с redux, с первого раза эта штука выглядит магией, потому что ключ части состояния (state.todos) соответствует функции, названной также только по желанию разработчика (function todos()<>).

Если бы нам нужно было написать такой функционал на нашем micro-redux, мы бы сделали так:

Этот код плохо масштабируется. Если у нас 2 «под-состояния», нам нужно дважды написать (state, action), а хорошие программисты так не делают, правда?

В следующем примере от вас ожидается, что вы не испугаетесь метода Object.entries и Деструктуризации параметров функции

Однако реализация метода combineReducers довольно простая (напоминаю, это если убрать валидацию и вывод ошибок) и самую малость отрефакторить на свой вкус:

Мы добавили к нашему детёнышу redux ещё 9 строк и массу удобства.

Перейдём к ещё одной важной фиче, которая кажется слишком сложной, чтобы пройти мимо неё.

applyMiddleware

middleware в разрезе redux — это какая-то штука, которая слушает все dispatch и при определенных условиях делает что-то. Логирует, проигрывает звуки, делает запросы к серверу,… — что-то.

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

При этом реализация метода applyMiddleware, когда ты потратишь 10 минут на ковыряние в чужом коде, сводится к очень простой вещи: createStore возвращает объект с полем «dispatch». dispatch, как мы помним (не помним) из первого листинга кода, — это функция, которая всего лишь применяет редюсер к нашему текущему состоянию (newState = reducer(state, action)).
Так вот applyMiddleware не более чем переопределяет метод dispatch, добавляя перед (или после) обновлением состояния какую-то пользовательскую логику.

Возьмём, например, самый популярный middleware от создателей redux — redux-thunk

Его смысл сводится к тому, что можно делать не только

но и передавать в store.dispatch сложные функции

И теперь, когда мы выполним команду

я понимаю, что конструкция выглядит жутковато, но её тоже просто нужно вызвать пару раз с произвольными параметрами и вы осознаете, что всё не так страшно, это просто функция, возвращающая функцию, возвращающую функцию (ладно, согласен, страшно)

Напомню, оригинальный метод createStore выглядел так

То есть он принимал атрибуты (reducer, initialState) и возвращал объект с ключами < dispatch, getState >.

Оказалось, что реализовать метод applyMiddleware проще, чем понять, как он работает.
Мы берём уже реализованный метод createStore и переопределяем его возвращаемое значение:

Вывод

Под капотом redux содержатся очень простые логические операции. Операции на уровне «Если бензин в цилиндре загорается, давление увеличивается». А вот то, сможете ли вы построить на этих понятиях болид Формулы 1 — уже решайте сами.

Источник

Пишем свой middleware для Redux

Доброго времени суток, друзья. В данной статье мы разберем понятие middleware в Redux и напишем свою небольшую Redux-middleware для логирования. Давайте обо всем по порядку.

Что такое middleware и зачем они нужны?

Само понятие middleware довольно-таки старая концепция. Если посмотреть на понятие в общем, то это промежуточное программное обеспечение для среднего слоя программы.

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

Давайте теперь посмотрим на это понятие в Redux. Redux-middleware является функция, которая получает данные после отправки экшена. Далее она может их проверить или сделать что-то с ними и затем передать дальше по цепочке в редьюсер.

Читайте также:  с каким цветом сочетается лаймовый цвет в одежде

Благодаря middleware в Redux имеется замечательная возможность производить нужные промежуточные действия в момент, когда пользователь взаимодействует с интерфейсом. Например, мы можем логировать данные, получать дополнительные данные, создавать нужные задержки и тд.

Пишем свою миделвару Logger для Redux

Давайте перейдем наконец к практике и напишем свою миделвару. Для этого создадим функцию logger, которая принимает в параметрах весь store. Функция logger будет возвращать анонимную функцию и получать в аргументе функцию next, которая на самом деле является функцией dispatch. После чего нам нужно вернуть функцию next с переданным в нее параметром action. Звучит немного запутанно, поэтому давайте опишем эту конструкцию в коде, используя ES5.

В самой функции нам потребуется воспроизвести следующую логику:

1. Проверяем начальное состояние store

2. Проверяем action

3. Передаем через параметр функции next полученный action для передачи его редьюсеру

4. Снова проверяем store

5. Возвращаем результат next(action) из функции logger

У вас может появиться вопрос: «Откуда в аргументах logger вдруг появился доступ к store, функциям next и action?» Все благодаря функции applyMiddleware и замыканию. Каждый последующий вызов функции logger получает из области видимости функции applyMiddleware новые параметры, которые можно использовать внутри самой миделвары.

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

В коде выше для визуальной группировки логов используются не очень распространенные функции console.groupCollapsed() и console.groupEnd().

Как добавить миделвару в Redux?

Чтобы инициализировать миделвару logger в приложении, нужно добавить ее в функцию applyMiddleware, которая импортируется из самого redux, и передать вторым параметром в createStore.

Важно! Количество миделвар, которые можно передать в applyMiddleware не ограничено, их можно добавлять сколько угодно, через запятую.

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

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

Пример нескольких middleware

Для большей наглядности приведу ниже несколько примеров Redux-middleware, которые могут быть вам полезны.

1. Позволяет отправлять промисы с экшеном. Если промис resolve, то его ответ будет отправлен как action.

2. Позволяет использовать функцию вместо экшена. Данная миделвар является очень популярной и велика вероятность, что вы с ней уже знакомы.

3. Позволяет отправлять action, в который можно передать количество миллисекунд в поле meta.delay для задержки выполнения данного экшена.

Заключение

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

Подписывайтесь на наш канал в Telegram и на YouTube для получения самой последней и актуальной информации.

Источник

Mидлвар¶

Вы видели мидлвары (Middleware) в действии в примере асинхронных экшенов. Если вы когда-либо использовали такие серверные библиотеки, как Express и Koa, то, вероятно, вы уже хорошо знакомы с концепцией мидлвар. В этих фреймворках мидлвары — это части кода, которые вы можете поместить между фреймворком, принимающим запрос и фреймворком, генерирующим ответ. Например, мидлвары из Express или Koa могут добавлять CORS-заголовки, логирование, сжатие и т. д. Лучшая особенность мидлваров заключается в том, что их можно соединять в цепочки/последовательности. Вы можете использовать множество независимых сторонних мидлваров в одном проекте.

Redux-мидлвары, в отличие от мидлваров Express или Koa, решают немного другие проблемы, но концептуально схожим способом. Они предоставляют стороннюю точку расширения, между отправкой экшена и моментом, когда этот экшен достигает редьюсера. Люди используют Redux-мидлвары для логирования, сообщения об ошибках, общения с асинхронным API, роутинга и т.д.

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

Понимание мидлваров¶

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

Проблема: логирование¶

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

Разве не было бы хорошо, если бы мы записывали каждое действие, которое происходило в приложении, вместе с состоянием, которое было вычислено после этого действия? Когда что-то идет не так, мы можем просмотреть наш лог и понять, какой именно экшен испортил наше состояние.

Как мы подходим к этому с Redux?

Попытка #1: Логируем вручную¶

Если вы используете react-redux или похожий биндинг, у вас, скорее всего, не будет прямого доступа к экземпляру стора в ваших компонентах. Для следующих нескольких параграфов представьте, что вы передаете состояние явно.

Читайте также:  что делать если в глаз попал пух

Например, вы вызываете такой код, когда создаете todo-элемент:

Для того чтобы логировать экшен и состояние, вы можете изменить код примерно так:

Это даст желаемый эффект, но вы бы не хотели делать так каждый раз.

Попытка #2: Оборачиваем Dispatch¶

Вы можете вынести логирование в функцию:

Вы можете использовать ее везде вместо обычного store.dispatch() :

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

Попытка #3: Monkeypatching для Dispatch¶

Что, если мы просто заменим функцию dispatch в экземпляре стора? Redux стор — это простой объект с парой методов, а мы пишем на JavaScript, следовательно, мы можем применить технику monkeypatch для реализации dispatch :

Это уже ближе к тому, что нам нужно! Не важно, откуда мы посылаем экшен, он гарантированно будет залогирован. Monkeypatching никогда не покажется правильным ходом, но пока мы можем с этим жить.

Проблема: Сообщения об ошибках.¶

Другое такое изменение, которое приходит мне в голову, это сообщения о JavaScript-ошибках в продакшене. Глобальное событие window.onerror не надежно потому, что оно в некоторых старых браузерах не предоставляет информацию о стеке вызовов, которая важна для понимания того, почему же произошла ошибка.

Разве не было бы полезно, если бы каждый раз, когда ошибка выбрасывалась, как результат отправки какого-либо экшена, мы могли бы отправить ее (ошибку), вместе со стеком вызовов, экшеном, который вызвал ошибку и актуальным состоянием в сервис сообщения об ошибках, такой как Sentry. В таком случае гораздо легче воспроизвести ошибку в разработке.

Однако важно, чтобы мы держали логирование и сообщения об ошибках раздельно. В идеальном случае, мы хотим получить их, как разные модули из разных пакетов. В противном случае, мы не сможем иметь экосистему из такого рода утилит. (Подсказка: мы медленно подходим к тому, что такое мидлвары!)

Если логирование и сообщения об ошибках являются отдельными утилитами, то они могут выглядеть так:

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

Но это все еще не очень хорошо.

Попытка #4: Прячем Monkeypatching¶

Мы могли бы предоставить функцию-помощник внутри Redux, которая могла бы применять актуальный monkeypatching, как часть имплементации:

Мы можем использовать такой подход для применения нескольких мидлваров:

Попытка #5: Убираем Monkeypatching¶

Это важно для возможности объединять мидлвары в цепочки!

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

Это тот момент, когда “we need to go deeper”, так что имеет смысл потратить некоторе время на это. Каскад функций выглядит пугающим. Стрелочные функции из ES6 делают это каррирование чуть более простым для глаз:

Именно так выглядят мидлвары в Redux.

Попытка #6: Простейшее применение мидлваров¶

Предостережение: отправка во время установки

Финальный подход¶

Дан мидлвар который мы только что написали:

Вот так можно его применить к Redux стору:

Вот и все! Теперь любые экшены, отправленные в экземпляр стора, будут проходить через logger и crashReporter :

Семь примеров¶

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

Каждая из функций, приведенных ниже, является валидным Redux-мидлваром. Они не являются в равной степени полезными, но, по крайней мере, они в равной степени забавны.

Источник

Mидлвар (Middleware)

Вы видели мидлвары в действии в примере асинхронных экшенов. Если вы когда-либо использовали такие серверные библиотеки, как Express и Koa, то, вероятно, вы уже хорошо знакомы с концепцией мидлвар. В этих фреймворках мидлвары — это части кода, которые вы можете поместить между фреймворком, принимающим запрос и фреймворком, генерирующим ответ. Например, мидлвары из Express или Koa могут добавлять CORS-заголовки, логирование, сжатие и т.д. Лучшая особенность мидлваров заключается в том, что их можно соединять в цепочки/последовательности. Вы можете использовать множество независимых сторонних мидлваров в одном проекте.

Redux-мидлвары, в отличие от мидлваров Express или Koa, решают немного другие проблемы, но концептуально схожим способом. Они предоставляют стороннюю точку расширения, между отправкой экшена и моментом, когда этот экшен достигает редюсера. Люди используют Redux-мидлвары для логирования, сообщения об ошибках, общения с асинхронным API, роутинга и т.д.

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

Понимание мидлваров

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

Проблема: логирование

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

Читайте также:  fuze tea это что

Разве не было бы хорошо, если бы мы записывали каждое действие, которое происходило в приложении, вместе с состоянием, которое было вычислено после этого действия? Когда что-то идет не так, мы можем просмотреть наш лог и понять, какой именно экшен испортил наше состояние.

Как мы подходим к этому с Redux?

Попытка #1: Логируем вручную

Обратите внимание

Если вы используете react-redux или похожий биндинг, у вас, скорее всего, не будет прямого доступа к экземпляру стора в ваших компонентах. Для следующих нескольких параграфов представьте, что вы передаете состояние явно.

Например, вы вызываете такой код, когда создаете todo-элемент:

Для того чтобы логировать экшен и состояние, вы можете изменить код примерно так:

Это даст желаемый эффект, но вы бы не хотели делать так каждый раз.

Попытка #2: Оборачиваем Dispatch

Вы можете вынести логирование в функцию:

Вы можете использовать ее везде вместо обычного store.dispatch() :

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

Попытка #3: Monkeypatching для Dispatch

Что, если мы просто заменим функцию dispatch в экземпляре стора? Redux стор — это простой объект с парой методов, а мы пишем на JavaScript, следовательно, мы можем применить технику monkeypatch для реализации dispatch :

Это уже ближе к тому, что нам нужно! Не важно, откуда мы посылаем экшен, он гарантированно будет залогирован. Monkeypatching никогда не покажется правильным ходом, но пока мы можем с этим жить.

Проблема: Сообщения об ошибках.

Другое такое изменение, которое приходит мне в голову, это сообщения о JavaScript-ошибках в продакшене. Глобальное событие window.onerror не надежно потому, что оно в некоторых старых браузерах не предоставляет информацию о стеке вызовов, которая важна для понимания того, почему же произошла ошибка.

Разве не было бы полезно, если бы каждый раз, когда ошибка выбрасывалась, как результат отправки какого-либо экшена, мы могли бы отправить ее (ошибку), вместе со стеком вызовов, экшеном, который вызвал ошибку и актуальным состоянием в сервис сообщения об ошибках, такой как Sentry. В таком случае гораздо легче воспроизвести ошибку в разработке.

Однако важно, чтобы мы держали логирование и сообщения об ошибках раздельно. В идеальном случае, мы хотим получить их, как разные модули из разных пакетов. В противном случае, мы не сможем иметь экосистему из такого рода утилит. (Подсказка: мы медленно подходим к тому, что такое мидлвары!)

Если логирование и сообщения об ошибках являются отдельными утилитами, то они могут выглядеть так:

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

Но это все еще не очень хорошо.

Попытка #4: Прячем Monkeypatching

Мы могли бы предоставить функцию-помощник внутри Redux, которая могла бы применять актуальный monkeypatching, как часть имплементации:

Мы можем использовать такой подход для применения нескольких мидлваров:

Попытка #5: Убираем Monkeypatching

Это важно для возможности объединять мидлвары в цепочки!

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

Это тот момент, когда “we need to go deeper”, так что имеет смысл потратить некоторе время на это. Каскад функций выглядит пугающим. Стрелочные функции из ES6 делают это каррирование чуть более простым для глаз:

Именно так выглядят мидлвары в Redux.

Попытка #6: Простейшее применение мидлваров

Предостережение: отправка во время установки

Финальный подход

Дан мидлвар который мы только что написали:

Вот так можно его применить к Redux стору:

Вот и все! Теперь любые экшены, отправленные в экземпляр стора, будут проходить через logger и crashReporter :

Семь примеров

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

Каждая из функций, приведенных ниже, является валидным Redux-мидлваром. Они не являются в равной степени полезными, но, по крайней мере, они в равной степени забавны.

Источник

applyMiddleware(. middlewares)

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

Например, redux-thunk позволяет генераторам экшенов инвертировать управление вызывая функции. Они будут получать dispatch как аргумент и могут вызывать его асинхронно. Такие функции называются преобразователями (thunks). Другим примером мидлвара является redux-promise. Он позволяет вам вызывать асинхронный экшен c Promise и вызывать обычные экшены, когда промис вернет resolve.

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

Параметры

Возвращает

Пример: мидлвар для кастомного логирования

Пример: Использование Thunk мидлвара для асинхронных экшенов

Советы

Если вы ходите применять мидлвар по условию, убедитесь, что он импортируется только тогда, когда он необходим:

Это позволяет инструментам сборки эффективно удалять неиспользуемый код из сборки и уменьшать их размер.

Вы можете использовать compose() для применения нескольких расширителей стора одновременно.

Источник

Сказочный портал