У меня есть действие, которое обновляет состояние уведомления моего приложения. Обычно это уведомление будет ошибкой или какой-либо информацией. Мне нужно затем через 5 секунд отправить еще одно действие, которое вернет исходное состояние уведомления, поэтому уведомления нет. Основная причина этого заключается в обеспечении функциональности, при которой уведомления автоматически исчезают через 5 секунд.
Мне не повезло с использованием setTimeout
и возвратом другого действия, и я не могу найти, как это делается в Интернете. Так что любые советы приветствуются.
redux-saga
основанный ответ, если вы хотите что-то лучше, чем гром. Поздний ответ, так что вам придется долго прокручивать его перед тем, как его увидеть :) Это не значит, что читать не стоит. Вот кратчайший путь: stackoverflow.com/a/38574266/82609Ответы:
Не попадайтесь в ловушку, думая, что библиотека должна прописывать, как все делать . Если вы хотите что-то сделать с тайм-аутом в JavaScript, вам нужно использовать
setTimeout
. Нет никаких причин, по которым действия Redux должны быть другими.Redux делает предлагает альтернативные способы борьбы с асинхронными вещами, но вы должны использовать только те , когда вы понимаете , что вы повторяете слишком много коды. Если у вас нет этой проблемы, используйте то, что предлагает язык, и найдите самое простое решение.
Написание асинхронного кода Inline
Это, безусловно, самый простой способ. И здесь нет ничего конкретного для Redux.
Аналогично, изнутри подключенного компонента:
Единственное отличие состоит в том, что в подключенном компоненте у вас обычно нет доступа к самому хранилищу, но вы получаете либо одного,
dispatch()
либо конкретного создателя действия, вводимого в качестве реквизита. Однако это не имеет никакого значения для нас.Если вам не нравится делать опечатки при отправке одних и тех же действий из разных компонентов, вы можете извлечь создателей действий вместо того, чтобы отправлять встроенные объекты действий:
Или, если вы ранее связали их с
connect()
:До сих пор мы не использовали промежуточное программное обеспечение или другие передовые концепции.
Извлечение Async Action Creator
Приведенный выше подход хорошо работает в простых случаях, но вы можете обнаружить, что у него есть несколько проблем:
HIDE_NOTIFICATION
, ошибочно скрывая второе уведомление раньше, чем после тайм-аута.Чтобы решить эти проблемы, вам нужно извлечь функцию, которая централизует логику тайм-аута и отправляет эти два действия. Это может выглядеть так:
Теперь компоненты могут использовать,
showNotificationWithTimeout
не дублируя эту логику или не имея условий гонки с различными уведомлениями:Почему
showNotificationWithTimeout()
принимаетdispatch
в качестве первого аргумента? Потому что для этого нужно отправлять действия в магазин. Обычно компонент имеет доступ к нему,dispatch
но поскольку мы хотим, чтобы внешняя функция контролировала диспетчеризацию, нам нужно предоставить ему контроль над диспетчеризацией.Если вы экспортировали одно хранилище из какого-то модуля, вы можете просто импортировать его и
dispatch
вместо этого прямо в него:Это выглядит проще, но мы не рекомендуем такой подход . Основная причина, по которой нам это не нравится, заключается в том, что он заставляет магазин быть синглтоном . Это очень затрудняет реализацию рендеринга сервера. . На сервере вы хотите, чтобы каждый запрос имел свое собственное хранилище, чтобы разные пользователи получали разные предварительно загруженные данные.
Магазин Singleton также усложняет тестирование. Вы больше не можете издеваться над магазином при тестировании создателей действий, поскольку они ссылаются на конкретный реальный магазин, экспортированный из определенного модуля. Вы даже не можете сбросить его состояние снаружи.
Поэтому, хотя технически вы можете экспортировать одноэлементное хранилище из модуля, мы не рекомендуем его. Не делайте этого, если вы не уверены, что ваше приложение никогда не добавит рендеринг сервера.
Возвращаясь к предыдущей версии:
Это решает проблемы с дублированием логики и спасает нас от условий гонки.
Thunk Middleware
Для простых приложений подход должен быть достаточным. Не беспокойтесь о промежуточном программном обеспечении, если вы довольны им.
В больших приложениях, однако, вы можете найти определенные неудобства вокруг него.
Например, кажется неудачным, что мы должны пройти мимо
dispatch
. Это усложняет разделение контейнерных и презентационных компонентов, потому что любой компонент, который отправляет действия Redux асинхронно описанным выше способом, должен принятьdispatch
в качестве подпорки, чтобы он мог пройти дальше. Вы больше не можете просто связывать создателей действийconnect()
, потому что наshowNotificationWithTimeout()
самом деле они не являются создателями действий. Он не возвращает действие Redux.Кроме того, может быть неудобно вспоминать, какие функции похожи на создатели синхронных действий,
showNotification()
а какие - на асинхронные помощникиshowNotificationWithTimeout()
. Вы должны использовать их по-разному и быть осторожным, чтобы не перепутать их друг с другом.Это послужило мотивацией для поиска способа «узаконить» этот шаблон предоставления
dispatch
вспомогательной функции и помочь Redux «увидеть» таких создателей асинхронных действий как особый случай создателей обычных действий, а не совершенно разные функции.Если вы все еще с нами, и вы также признаете проблему в своем приложении, вы можете использовать промежуточное ПО Redux Thunk .
В сущности, Redux Thunk учит Redux распознавать особые виды действий, которые на самом деле являются функциями:
Когда это промежуточное программное обеспечение включено, если вы отправляете функцию , промежуточное программное обеспечение Redux Thunk предоставит ее
dispatch
в качестве аргумента. Он также «проглотит» такие действия, так что не беспокойтесь о том, что ваши редукторы получают странные аргументы функций. Ваши редукторы будут получать только простые действия с объектами - либо испускаемые напрямую, либо испускаемые функциями, как мы только что описали.Это выглядит не очень полезно, не так ли? Не в этой конкретной ситуации. Однако это позволяет нам объявить
showNotificationWithTimeout()
себя обычным создателем действий Redux:Обратите внимание, что функция практически идентична той, которую мы написали в предыдущем разделе. Однако это не принимает
dispatch
в качестве первого аргумента. Вместо этого он возвращает функцию, которая принимаетdispatch
в качестве первого аргумента.Как бы мы использовали его в нашем компоненте? Определенно, мы могли бы написать это:
Мы вызываем создателя асинхронного действия, чтобы получить внутреннюю функцию, которая хочет просто
dispatch
, и затем мы переходимdispatch
.Однако это даже более неловко, чем оригинальная версия! Почему мы даже пошли по этому пути?
Из-за того, что я говорил тебе раньше. Если промежуточное программное обеспечение Redux Thunk включено, то при каждой попытке отправки функции вместо объекта действия промежуточное программное обеспечение будет вызывать эту функцию с
dispatch
самим методом в качестве первого аргумента .Таким образом, мы можем сделать это вместо этого:
Наконец, отправка асинхронного действия (на самом деле, серии действий) выглядит не иначе, как синхронная отправка одного действия компоненту. Это хорошо, потому что компоненты не должны заботиться о том, происходит ли что-то синхронно или асинхронно. Мы просто абстрагировали это.
Обратите внимание, что, поскольку мы «научили» Redux распознавать таких «создателей специальных действий» (мы называем их создателями « большого действия»), мы можем теперь использовать их в любом месте, где мы будем использовать создателей обычных действий. Например, мы можем использовать их с
connect()
:Состояние чтения в Thunks
Обычно ваши редукторы содержат бизнес-логику для определения следующего состояния. Однако редукторы включаются только после отправки действий. Что если у вас есть побочный эффект (например, вызов API) в создателе thunk action, и вы хотите предотвратить его при определенных условиях?
Без использования промежуточного программного обеспечения Thunk, вы просто выполните эту проверку внутри компонента:
Тем не менее, целью извлечения создателя действия была централизация этой повторяющейся логики во многих компонентах. К счастью, Redux Thunk предлагает вам прочитать текущее состояние магазина Redux. В дополнение к
dispatch
этому он также передаетсяgetState
в качестве второго аргумента функции, которую вы возвращаете создателю thunk action. Это позволяет thunk прочитать текущее состояние магазина.Не злоупотребляйте этим паттерном. Это хорошо для спасения вызовов API, когда доступны кэшированные данные, но это не очень хорошая основа для построения вашей бизнес-логики. Если вы используете
getState()
только условную диспетчеризацию различных действий, вместо этого поместить бизнес-логику в редукторы.Следующие шаги
Теперь, когда у вас есть базовая интуиция о том, как работают thunks, посмотрите пример асинхронного Redux, который их использует.
Вы можете найти много примеров, в которых thunks возвращают обещания. Это не обязательно, но может быть очень удобно. Redux не заботится о том, что вы возвращаете из thunk, но дает вам возвращаемое значение
dispatch()
. Вот почему вы можете вернуть Обещание из thunk и дождаться его завершения, позвонивdispatch(someThunkReturningPromise()).then(...)
.Вы также можете разделить сложных создателей Thunk Action на несколько меньших создателей Thunk Action.
dispatch
Метод , предоставляемый санков может принять санки самого по себе, так что вы можете применить шаблон рекурсивно. Опять же, это лучше всего работает с Promises, потому что вы можете реализовать асинхронный поток управления поверх этого.Для некоторых приложений вы можете оказаться в ситуации, когда ваши требования к потоку асинхронного управления слишком сложны, чтобы их можно было выразить с помощью групповых символов. Например, повторная попытка неудачных запросов, поток повторной авторизации с токенами или пошаговая регистрация могут быть слишком многословными и подверженными ошибкам при написании таким образом. В этом случае вы можете захотеть взглянуть на более продвинутые решения асинхронного потока управления, такие как Redux Saga или Redux Loop . Оцените их, сравните примеры, соответствующие вашим потребностям, и выберите тот, который вам нравится больше всего.
Наконец, не используйте ничего (включая громоотводы), если у вас нет в них реальной необходимости. Помните, что в зависимости от требований ваше решение может выглядеть так же просто, как
Не переживайте, если не знаете, почему вы это делаете.
источник
if (cond) dispatch({ type: 'A' }) else dispatch({ type: 'B' })
может быть, вам следует простоdispatch({ type: 'C', something: cond })
выбрать и игнорировать действие в редукторах, в зависимости отaction.something
текущего состояния.Использование Redux-саги
Как сказал Дан Абрамов, если вы хотите более расширенный контроль над вашим асинхронным кодом, вы можете взглянуть на redux-saga .
Этот ответ является простым примером. Если вы хотите получить более подробные объяснения того, почему redux-saga может быть полезен для вашего приложения, проверьте этот другой ответ .
Общая идея заключается в том, что Redux-saga предлагает интерпретатор генераторов ES6, который позволяет вам легко писать асинхронный код, который выглядит как синхронный код (вот почему вы часто найдете бесконечные циклы while в Redux-saga). Каким-то образом Redux-saga создает свой собственный язык прямо внутри Javascript. Поначалу Redux-saga может показаться немного сложным в изучении, потому что вам нужно базовое понимание генераторов, но также и понимание языка, предлагаемого Redux-saga.
Я постараюсь здесь описать систему уведомлений, которую я построил на основе redux-saga. Этот пример в настоящее время работает в производстве.
Спецификация расширенной системы уведомлений
Результат
Скриншот моего производственного приложения Stample.co
Код
Здесь я назвал уведомление a,
toast
но это деталь именования.И редуктор:
Применение
Вы можете просто отправлять
TOAST_DISPLAY_REQUESTED
события. Если вы отправите 4 запроса, будут отображены только 3 уведомления, а четвертый появится чуть позже, как только исчезнет 1-е уведомление.Обратите внимание, что я не рекомендую отправлять
TOAST_DISPLAY_REQUESTED
из JSX. Вы бы предпочли добавить еще одну сагу, которая прослушивает уже существующие события приложения, а затем отправитьTOAST_DISPLAY_REQUESTED
: ваш компонент, который запускает уведомление, не должен быть тесно связан с системой уведомлений.Вывод
Мой код не идеален, но работает с 0 ошибками в течение нескольких месяцев. Redux-saga и генераторы изначально немного сложны, но как только вы их поймете, такую систему довольно просто построить.
Даже довольно легко реализовать более сложные правила, такие как:
Честно говоря, удачи в правильной реализации такого рода вещей с помощью thunks.
Заметьте, что вы можете делать точно такие же вещи с redux-observable, что очень похоже на redux-saga. Это почти то же самое, и дело вкуса между генераторами и RxJS.
источник
yield call(delay,timeoutValue);
: это не тот же API, но он имеет тот же эффектРепозиторий с примерами проектов
На данный момент существует четыре примера проектов:
Принятый ответ потрясающий.
Но чего-то не хватает:
Поэтому я создал репозиторий Hello Async, чтобы добавить недостающие элементы:
Redux Saga
Принятый ответ уже содержит примеры фрагментов кода для Async Code Inline, Async Action Generator и Redux Thunk. Для полноты картины приведу фрагменты кода для Redux Saga:
Действия просты и чисты.
Ничего особенного с компонентом.
Саги основаны на генераторах ES6
По сравнению с Redux Thunk
Pros
Cons
Пожалуйста, обратитесь к готовому проекту, если приведенные выше фрагменты кода не отвечают на все ваши вопросы.
источник
Вы можете сделать это с помощью redux-thunk . В избыточном документе есть руководство по асинхронным действиям, таким как setTimeout.
источник
applyMiddleware(ReduxPromise, thunk)(createStore)
, вы добавляете несколько промежуточных программ (разделенных комой?), Так как мне кажется, что работать не получается.const store = createStore(reducer, applyMiddleware([ReduxPromise, thunk]));
Я бы порекомендовал также взглянуть на модель SAM .
Шаблон SAM требует включения «предиката следующего действия», при котором (автоматические) действия, такие как «уведомления исчезают автоматически через 5 секунд», запускаются после обновления модели (модель SAM ~ состояние редуктора + хранилище).
Шаблон поддерживает последовательное выполнение действий и мутаций модели по одному, потому что «управляющее состояние» модели «контролирует», какие действия включены и / или автоматически выполняются предикатом следующего действия. Вы просто не можете предсказать (в общем), в каком состоянии будет система до обработки действия и, следовательно, будет ли разрешено / возможно ваше следующее ожидаемое действие.
Так, например, код,
не будет разрешено с SAM, потому что тот факт, что действие hideNotification может быть отправлено, зависит от модели, успешно принимающей значение "showNotication: true". Могут быть другие части модели, которые не позволяют ей принять его, и, следовательно, не будет причин для запуска действия hideNotification.
Я настоятельно рекомендую реализовать надлежащий предикат следующего действия после того, как магазин обновит и новое состояние управления моделью станет известно. Это самый безопасный способ реализовать поведение, которое вы ищете.
Вы можете присоединиться к нам на Gitter, если хотите. Здесь также есть руководство по началу работы с SAM .
источник
V = S( vm( M.present( A(data) ) ), nap(M))
это просто красиво. Спасибо, что поделились своими мыслями и опытом. Я буду копать глубже.Попробовав различные популярные подходы (создатели действий, сценарии, саги, эпики, эффекты, пользовательское промежуточное программное обеспечение), я все еще чувствовал, что, возможно, есть место для улучшений, поэтому я задокументировал свое путешествие в этой статье блога, куда я положил свою бизнес-логику приложение React / Redux?
Как и обсуждения здесь, я попытался сопоставить и сравнить различные подходы. В конечном итоге это привело меня к представлению новой библиотеки лексемы-логики, которая черпает вдохновение из эпосов, саг, специального промежуточного программного обеспечения.
Он позволяет вам перехватывать действия для проверки, подтверждения, авторизации, а также предоставлять способ выполнения асинхронного ввода-вывода.
Некоторые общие функции могут быть просто объявлены как отмена, регулирование, отмена и только использование ответа от последнего запроса (takeLatest). Избыточная логика оборачивает ваш код, предоставляя вам эту функциональность.
Это освобождает вас от реализации вашей основной бизнес-логики так, как вам нравится. Вам не нужно использовать наблюдаемые или генераторы, если вы не хотите. Используйте функции и обратные вызовы, обещания, асинхронные функции (async / await) и т. Д.
Код для простого уведомления 5s будет выглядеть примерно так:
У меня есть более продвинутый пример уведомления в моем репо, который работает аналогично тому, что описал Себастьян Лорбер, где вы можете ограничить отображение до N элементов и поворачивать все, что находится в очереди. пример уведомления с избыточной логикой
У меня есть множество примеров живой редукционной логики jsfiddle, а также полные примеры . Я продолжаю работать над документами и примерами.
Я хотел бы услышать ваши отзывы.
источник
Я понимаю, что этот вопрос немного устарел, но я собираюсь представить другое решение, использующее избыточно наблюдаемый aka. Эпическая.
Цитирование официальной документации:
Что такое наблюдаемый редукс?
Эпос является основным примитивом наблюдаемого редукса.
Другими словами, вы можете создать функцию, которая получает действия через поток и затем возвращает новый поток действий (с использованием общих побочных эффектов, таких как тайм-ауты, задержки, интервалы и запросы).
Позвольте мне опубликовать код, а затем объяснить немного больше об этом
store.js
index.js
App.js
Код ключа для решения этой проблемы так же прост, как и круговая диаграмма, единственное, что отличается от других ответов, - это функция rootEpic.
Пункт 1. Как и в случае саг, вы должны объединить эпосы, чтобы получить функцию верхнего уровня, которая получает поток действий и возвращает поток действий, поэтому вы можете использовать ее с фабрикой промежуточного программного обеспечения createEpicMiddleware . В нашем случае нам нужен только один, поэтому у нас есть только rootEpic, поэтому нам не нужно ничего комбинировать, но это хорошо знать.
Пункт 2. Наш rootEpic который заботится о логике побочных эффектов, занимает всего около 5 строк кода, и это здорово! Включая тот факт, что это в значительной степени декларативно!
Пункт 3. Построчно rootEpic объяснение (в комментариях)
Я надеюсь, что это помогает!
источник
switchMap
?Почему это должно быть так сложно? Это просто логика интерфейса. Используйте выделенное действие для установки данных уведомления:
и выделенный компонент для его отображения:
В этом случае вопросы должны быть «как очистить старое состояние?», «Как уведомить компонент, что время изменилось»
Вы можете реализовать некоторое действие TIMEOUT, которое отправляется на setTimeout из компонента.
Может быть, это просто, чтобы очистить его всякий раз, когда отображается новое уведомление.
Во всяком случае, должно быть
setTimeout
где-то, верно? Почему бы не сделать это в компонентеМотивация заключается в том, что функциональность «исчезновения уведомлений» действительно является проблемой пользовательского интерфейса. Это упрощает тестирование вашей бизнес-логики.
Кажется, нет смысла проверять, как это реализовано. Имеет смысл проверить, когда время ожидания уведомления истекло. Таким образом, меньше кода на заглушку, быстрее тесты, более чистый код.
источник
Если вы хотите обработать тайм-аут при выборочных действиях, вы можете попробовать промежуточное ПО подход . Я столкнулся с аналогичной проблемой для выборочной обработки действий, основанных на обещаниях, и это решение было более гибким.
Допустим, ваш создатель действий выглядит так:
Тайм-аут может содержать несколько значений в вышеуказанном действии
Ваша реализация промежуточного программного обеспечения будет выглядеть так:
Теперь вы можете направлять все свои действия через этот промежуточный слой, используя приставку.
Вы можете найти несколько похожих примеров здесь
источник
Соответствующий способ сделать это - использовать Redux Thunk, который является популярным промежуточным ПО для Redux, согласно документации Redux Thunk:
Так что в основном он возвращает функцию, и вы можете отложить отправку или перевести ее в состояние условия.
Итак, что-то вроде этого сделает работу за вас:
источник
Это просто. Используйте пакет trim-redux и напишите так в этом
componentDidMount
или другом месте и убейте егоcomponentWillUnmount
.источник
Redux сам по себе является довольно многословной библиотекой, и для таких вещей вам придется использовать что-то вроде Redux-thunk , который даст
dispatch
функцию, так что вы сможете отправить уведомление о закрытии через несколько секунд.Я создал библиотеку для решения таких вопросов, как многословие и сочетаемость, и ваш пример будет выглядеть следующим образом:
Поэтому мы составляем синхронизирующие действия для отображения уведомлений внутри асинхронного действия, которое может запросить некоторую информацию в фоновом режиме или позже проверить, было ли уведомление закрыто вручную.
источник