Согласно документации, «без промежуточного программного обеспечения хранилище Redux поддерживает только синхронный поток данных» . Я не понимаю, почему это так. Почему компонент контейнера не может вызвать асинхронный API, а затем dispatch
действия?
Например, представьте себе простой интерфейс: поле и кнопку. Когда пользователь нажимает кнопку, поле заполняется данными с удаленного сервера.
import * as React from 'react';
import * as Redux from 'redux';
import { Provider, connect } from 'react-redux';
const ActionTypes = {
STARTED_UPDATING: 'STARTED_UPDATING',
UPDATED: 'UPDATED'
};
class AsyncApi {
static getFieldValue() {
const promise = new Promise((resolve) => {
setTimeout(() => {
resolve(Math.floor(Math.random() * 100));
}, 1000);
});
return promise;
}
}
class App extends React.Component {
render() {
return (
<div>
<input value={this.props.field}/>
<button disabled={this.props.isWaiting} onClick={this.props.update}>Fetch</button>
{this.props.isWaiting && <div>Waiting...</div>}
</div>
);
}
}
App.propTypes = {
dispatch: React.PropTypes.func,
field: React.PropTypes.any,
isWaiting: React.PropTypes.bool
};
const reducer = (state = { field: 'No data', isWaiting: false }, action) => {
switch (action.type) {
case ActionTypes.STARTED_UPDATING:
return { ...state, isWaiting: true };
case ActionTypes.UPDATED:
return { ...state, isWaiting: false, field: action.payload };
default:
return state;
}
};
const store = Redux.createStore(reducer);
const ConnectedApp = connect(
(state) => {
return { ...state };
},
(dispatch) => {
return {
update: () => {
dispatch({
type: ActionTypes.STARTED_UPDATING
});
AsyncApi.getFieldValue()
.then(result => dispatch({
type: ActionTypes.UPDATED,
payload: result
}));
}
};
})(App);
export default class extends React.Component {
render() {
return <Provider store={store}><ConnectedApp/></Provider>;
}
}
Когда экспортированный компонент отображается, я могу нажать на кнопку, и ввод корректно обновляется.
Обратите внимание на update
функцию в connect
вызове. Он отправляет действие, которое сообщает приложению об обновлении, а затем выполняет асинхронный вызов. После завершения вызова предоставленное значение отправляется как полезная нагрузка другого действия.
Что не так с этим подходом? Почему я хотел бы использовать Redux Thunk или Redux Promise, как следует из документации?
РЕДАКТИРОВАТЬ: Я искал в репозитории Redux подсказки, и обнаружил, что создатели действий должны были быть чистыми функциями в прошлом. Например, вот пользователь, пытающийся дать лучшее объяснение асинхронного потока данных:
Сам создатель действия все еще является чистой функцией, но возвращаемая им функция thunk не обязательна, и он может выполнять наши асинхронные вызовы.
Создатели действий больше не обязаны быть чистыми. Итак, промежуточное программное обеспечение Thunk / обещание определенно требовалось в прошлом, но кажется, что это больше не так?
источник
Ответы:
В этом подходе нет ничего плохого. Это просто неудобно в большом приложении, потому что у вас будут разные компоненты, выполняющие одни и те же действия, вы можете отменить некоторые действия или оставить некоторые локальные состояния, такие как автоматически увеличивающиеся идентификаторы, близкими к создателям действий и т. Д. с точки зрения обслуживания, чтобы выделить действия создателей в отдельные функции.
Вы можете прочитать мой ответ на «Как отправить действие Redux с тайм-аутом» для более подробного ознакомления.
Промежуточное программное обеспечение, такое как Redux Thunk или Redux Promise, просто дает вам «синтаксический сахар» для отправки thunks или обещаний, но вам не нужно его использовать.
Таким образом, без какого-либо промежуточного программного обеспечения ваш создатель действий может выглядеть
Но с Thunk Middleware вы можете написать это так:
Так что нет большой разницы. Что мне нравится в последнем подходе, так это то, что компоненту не важно, что создатель действий является асинхронным. Он просто вызывает
dispatch
нормально, его также можно использоватьmapDispatchToProps
для связывания таких создателей действий с коротким синтаксисом и т. Д. Компоненты не знают, как реализованы создатели действий, и вы можете переключаться между различными асинхронными подходами (Redux Thunk, Redux Promise, Redux Saga ) без замены компонентов. С другой стороны, при первом, явном подходе ваши компоненты точно знают , что конкретный вызов является асинхронным, и его необходимоdispatch
передать по некоторому соглашению (например, в качестве параметра синхронизации).Также подумайте, как изменится этот код. Скажем, мы хотим иметь вторую функцию загрузки данных и объединить их в одном создателе действий.
При первом подходе мы должны помнить о том, какого типа создателя действий мы называем:
С Redux Thunk создатели действий могут стать
dispatch
результатом действий других создателей и даже не думать, являются ли они синхронными или асинхронными:При таком подходе, если вы позже захотите, чтобы создатели ваших действий посмотрели на текущее состояние Redux, вы можете просто использовать второй
getState
аргумент, передаваемый thunks, вообще не изменяя вызывающий код:Если вам нужно изменить его на синхронный, вы также можете сделать это без изменения какого-либо вызывающего кода:
Таким образом, преимущество использования промежуточного программного обеспечения, такого как Redux Thunk или Redux Promise, состоит в том, что компоненты не знают о том, как реализованы создатели действий, и заботятся ли они о состоянии Redux, являются ли они синхронными или асинхронными, а также вызывают ли они других создателей действий. , Недостатком является некоторая косвенность, но мы считаем, что это стоит в реальных приложениях.
Наконец, Redux Thunk и друзья - это только один из возможных подходов к асинхронным запросам в приложениях Redux. Другим интересным подходом является Redux Saga, который позволяет вам определять длительно работающих демонов («саг»), которые выполняют действия по мере их поступления, а также преобразуют или выполняют запросы перед выводом действий. Это перемещает логику от создателей действий в саги. Вы можете проверить это, а потом выбрать то, что подходит вам больше всего.
Это неверно Документы сказали это, но документы были не правы.
Создатели действий никогда не должны были быть чистыми функциями.
Мы исправили документы, чтобы отразить это.
источник
alert
послеdispatch()
действия.loadData(this.props.dispatch, this.props.userId); // don't forget to pass dispatch
. Почему я должен пройти отправку? Если по соглашению существует только один глобальный магазин, почему бы мне просто не ссылаться на это напрямую и не делать этоstore.dispatch
всякий раз, когда мне нужно, например, вloadData
?store
экземпляры для каждого запроса, поэтому вы не можете определить его заранее.Вы не
Но ... вы должны использовать Redx-сагу :)
Ответ Дана Абрамова верен,
redux-thunk
но я расскажу немного больше о сундук-реду, который очень похож, но более силен.Повелительный указ декларативный
redux-thunk
обязательно /redux-saga
декларативноКогда у вас в руках есть Thunk, например IO-монада или обещание, вы не можете легко знать, что он будет делать после выполнения. Единственный способ проверить thunk - это выполнить его и высмеять диспетчера (или весь внешний мир, если он взаимодействует с большим количеством материала ...).
Если вы используете макеты, то вы не занимаетесь функциональным программированием.
Источник
Саги (в том виде, в
redux-saga
каком они были реализованы ) являются декларативными и, подобно компонентам Free monad или React, их гораздо проще тестировать без всяких насмешек.Смотрите также эту статью :
(На самом деле Redux-сага похожа на гибрид: поток обязателен, но эффекты декларативны)
Путаница: действия / события / команды ...
В мире внешнего интерфейса существует большая путаница в отношении того, как могут быть связаны некоторые базовые концепции, такие как CQRS / EventSourcing и Flux / Redux, главным образом потому, что в Flux мы используем термин «действие», который иногда может представлять как императивный код (
LOAD_USER
), так и события (USER_LOADED
). Я считаю, что, как и в случае с источником событий, вы должны только отправлять события.Использование саг на практике
Представьте себе приложение со ссылкой на профиль пользователя. Идиоматический способ справиться с этим с каждым промежуточным программным обеспечением:
redux-thunk
redux-saga
Эта сага переводится как:
Как видите, есть некоторые преимущества
redux-saga
.Использование
takeLatest
разрешений для выражения того, что вас интересует только получение данных о последнем щелчке имени пользователя (устранение проблем параллелизма в случае, если пользователь очень быстро нажимает на большое количество имен пользователей). Такого рода вещи сложны с громилой. Вы могли бы использовать,takeEvery
если вы не хотите этого поведения.Вы держите создателей действий в чистоте. Обратите внимание, что все еще полезно сохранять actionCreators (в сагах
put
и компонентахdispatch
), поскольку это может помочь вам добавить проверку действия (assertions / flow / typescript) в будущем.Ваш код становится намного более тестируемым, так как эффекты декларативны
Вам больше не нужно инициировать вызовы, похожие на rpc
actions.loadUser()
. Ваш пользовательский интерфейс просто должен отправить то, что произошло. Мы только запускаем события (всегда в прошедшем времени!), А не действия больше. Это означает, что вы можете создавать развязанные «утки» или ограниченные контексты и что сага может выступать в качестве связующего звена между этими модульными компонентами.Это означает, что вашими представлениями легче управлять, потому что им больше не нужно содержать слой перевода между тем, что произошло, и тем, что должно произойти как результат.
Например, представьте бесконечный вид прокрутки.
CONTAINER_SCROLLED
может привести к томуNEXT_PAGE_LOADED
, но действительно ли прокручиваемый контейнер несет ответственность за решение, следует ли нам загружать другую страницу? Затем он должен знать о более сложных вещах, таких как, была ли успешно загружена последняя страница, или уже есть страница, которая пытается загрузить, или нет больше элементов для загрузки? Я так не думаю: для максимального повторного использования прокручиваемый контейнер должен просто описывать, что он был прокручен. Загрузка страницы является «бизнес-эффектом» этого свиткаКто-то может возразить, что генераторы могут скрывать состояние за пределами хранилища избыточных данных с помощью локальных переменных, но если вы начнете организовывать сложные вещи внутри thunks, запуская таймеры и т.д., у вас все равно будет та же проблема. И есть
select
эффект, который теперь позволяет получить состояние из вашего магазина Redux.Sagas позволяет путешествовать во времени, а также обеспечивает сложную регистрацию потоков и инструменты разработки, над которыми в настоящее время ведутся работы. Вот некоторая простая асинхронная регистрация потока, которая уже реализована:
Развязка
Саги не только заменяют редуксных сундуков. Они поступают из бэкэнда / распределенных систем / источников событий.
Это очень распространенное заблуждение, что саги только здесь, чтобы заменить ваши редуксные гирлянды лучшей тестируемостью. На самом деле, это всего лишь деталь реализации Redx-саги. Использование декларативных эффектов лучше, чем thunk для тестируемости, но шаблон саги может быть реализован поверх императивного или декларативного кода.
Во-первых, сага - это часть программного обеспечения, которая позволяет координировать длительные транзакции (конечная согласованность) и транзакции в разных ограниченных контекстах (жаргон на основе доменного дизайна).
Чтобы упростить это для внешнего мира, представьте, что есть widget1 и widget2. Когда нажата какая-то кнопка на widget1, это должно повлиять на widget2. Вместо того, чтобы соединить 2 виджета вместе (т.е. widget1 отправляет действие, которое нацелено на widget2), widget1 отправляет только то, что была нажата его кнопка. Затем сага прослушивает нажатие этой кнопки, а затем обновляет widget2, отправляя новое событие, о котором известно widget2.
Это добавляет уровень косвенности, который не нужен для простых приложений, но упрощает масштабирование сложных приложений. Теперь вы можете публиковать widget1 и widget2 в разных репозиториях npm, чтобы им никогда не приходилось узнавать друг о друге, не предоставляя им общий глобальный реестр действий. 2 виджета теперь являются ограниченными контекстами, которые могут жить отдельно. Они не нуждаются друг в друге, чтобы быть последовательными и могут быть повторно использованы в других приложениях. Сага является связующим звеном между двумя виджетами, которые значимым образом координируют их для вашего бизнеса.
Несколько хороших статей о том, как структурировать ваше приложение Redux, в котором вы можете использовать Redux-saga по причинам разделения:
Конкретный вариант использования: система уведомлений
Я хочу, чтобы мои компоненты могли запускать отображение уведомлений в приложении. Но я не хочу, чтобы мои компоненты были тесно связаны с системой уведомлений, которая имеет свои собственные бизнес-правила (макс. 3 уведомления, отображаемые одновременно, очередь уведомлений, время отображения 4 секунды и т. Д.).
Я не хочу, чтобы мои компоненты JSX решали, когда будет отображаться / скрываться уведомление. Я просто даю ему возможность запросить уведомление и оставить сложные правила внутри саги. Подобные вещи довольно сложно реализовать с помощью благих обещаний.
Я описал здесь, как это можно сделать с помощью саги
Почему это называется сага?
Термин сага происходит из бэкэнд-мира. Я первоначально представил Yassine (автора Redux-saga) этому термину в длительной дискуссии .
Первоначально этот термин был введен в статье. Предполагалось, что шаблон saga будет использоваться для обработки возможной согласованности в распределенных транзакциях, но его использование было расширено до более широкого определения бэкэнд-разработчиками, так что теперь оно также охватывает «менеджер процессов». шаблон (каким-то образом оригинальный шаблон саги является специализированной формой менеджера процессов).
Сегодня термин «сага» сбивает с толку, поскольку он может описывать 2 разные вещи. Поскольку он используется в redux-saga, он описывает не способ обработки распределенных транзакций, а способ координации действий в вашем приложении.
redux-saga
также можно было бы назватьredux-process-manager
.Смотрите также:
альтернативы
Если вам не нравится идея использования генераторов, но вас интересует шаблон саги и его свойства развязки, вы также можете добиться того же с помощью redux-observable, которое использует имя
epic
для описания точно такого же шаблона, но с RxJS. Если вы уже знакомы с Rx, вы будете чувствовать себя как дома.Некоторые полезные ресурсы редукса-саги
2017 советует
yield put(someActionThunk)
если это имеет смысл.Если вы боитесь использовать Redux-saga (или Redux-observable), но вам нужен только шаблон развязки, проверьте redux-dispatch-subscribe : он позволяет прослушивать диспетчеризацию и запускать новые диспетчеры в приемнике.
источник
ADD_ITEM
, это обязательно, потому что вы отправляете действие, которое имеет целью повлиять на ваш магазин: вы ожидаете, что действие что-то сделает. Будучи декларативным, придерживайтесь философии поиска событий: вы не отправляете действия, чтобы вызвать изменения в ваших приложениях, но вы отправляете прошлые события, чтобы описать, что произошло в вашем приложении. Отправка события должна быть достаточной, чтобы считать, что состояние приложения изменилось. Тот факт, что есть магазин Redux, который реагирует на событие, является необязательной деталью реализацииКороткий ответ : мне кажется вполне разумным подход к проблеме асинхронности. С парой предостережений.
У меня была очень похожая точка зрения, когда я работал над новым проектом, который мы только начали на моей работе. Я был большим поклонником элегантной системы Vanilla Redux для обновления хранилища и рендеринга компонентов таким образом, чтобы не выходить за пределы дерева компонентов React. Мне показалось странным подключиться к этому элегантному
dispatch
механизму для обработки асинхронности.Я закончил тем, что пошел с очень похожим подходом к тому, что у вас есть в библиотеке, которую я учел в нашем проекте, который мы назвали response-redux-controller .
Я закончил тем, что не выбрал именно тот подход, который у вас есть выше, по нескольким причинам:
dispatch
себе через лексическую область. Это ограничивает возможности рефакторинга, как только этотconnect
оператор выходит из-под контроля - и это выглядит довольно громоздким только с одним этимupdate
методом. Поэтому вам нужна система, позволяющая вам составлять эти функции диспетчера, если вы разбиваете их на отдельные модули.Взятые вместе, вы должны настроить некоторую систему, чтобы позволить
dispatch
и хранилище быть введенным в ваши функции диспетчеризации, наряду с параметрами события. Мне известны три разумных подхода к внедрению этой зависимости:dispatch
подходами промежуточного программного обеспечения, но я предполагаю, что они в основном одинаковы.connect
, вместо того, чтобы работать непосредственно с необработанным нормализованным хранилищем.this
контекст, используя множество возможных механизмов.Обновить
Мне приходит в голову, что часть этой головоломки является ограничением реактивного редукса . Первый аргумент
connect
получает снимок состояния, но не диспетчеризацию. Второй аргумент получает отправку, но не состояние. Ни один из аргументов не получает блокировку, закрывающуюся по текущему состоянию, для возможности просмотра обновленного состояния во время продолжения / обратного вызова.источник
Цель Абрамова - и в идеале каждого - состоит в том, чтобы просто инкапсулировать сложность (и асинхронные вызовы) в месте, где это наиболее уместно .
Где лучшее место для этого в стандартном потоке данных Redux? Как насчет:
источник
Чтобы ответить на вопрос, который задают в начале:
Имейте в виду, что эти документы предназначены для Redux, а не для Redux плюс React. Хранилища Redux, подключенные к компонентам React, могут делать именно то, что вы говорите, но хранилище Plain Jane Redux без промежуточного программного обеспечения не принимает аргументы,
dispatch
кроме простых старых объектов.Без промежуточного программного обеспечения вы, конечно, можете обойтись
Но подобный случай , когда асинхронность обернута вокруг Redux , а не обрабатываются с помощью Redux. Таким образом, промежуточное ПО допускает асинхронность, изменяя то, что может быть передано напрямую
dispatch
.Тем не менее, дух вашего предложения, я думаю, действителен. Конечно, есть и другие способы обработки асинхронности в приложении Redux + React.
Одним из преимуществ использования промежуточного программного обеспечения является то, что вы можете продолжать использовать создателей действий как обычно, не беспокоясь о том, как они подключены. Например, используя
redux-thunk
код, который вы написали, будет выглядеть таккоторый не выглядит так уж сильно отличается от оригинала - он просто немного перемешан - и
connect
не знает, чтоupdateThing
это (или должно быть) асинхронно.Если вы также хотели поддерживать обещания , заметки , саги или сумасшедших создателей нестандартных и высоко декларативных действий, тогда Redux может сделать это, просто изменив то, что вы передаете
dispatch
(то есть то, что вы возвращаете от создателей действия). Нет необходимости в использовании компонентов React (илиconnect
вызовов).источник
Хорошо, давайте начнем с того, как сначала работает промежуточное ПО, что вполне отвечает на вопрос, это исходный код функции pplyMiddleWare в Redux:
Посмотрите на эту часть, посмотрите, как наша рассылка стала функцией .
Хорошо, вот как Redux-thunk как одно из наиболее часто используемых промежуточных программ для Redux представляет себя:
Итак, как вы видите, он будет возвращать функцию вместо действия, что означает, что вы можете ждать и вызывать ее в любое время, когда захотите, поскольку это функция ...
Так что, черт возьми, такое? Вот как это представлено в Википедии:
Посмотрите, насколько проста концепция и как она может помочь вам управлять вашими асинхронными действиями ...
Это то, что вы можете жить без этого, но помните, что в программировании всегда есть лучшие, аккуратные и правильные способы сделать что-то ...
источник
Использовать Redux-saga - лучшее промежуточное ПО в реализации React-redux.
Пример: store.js
А потом saga.js
А потом action.js
А потом Reducer.js
А потом main.js
попробуй это .. работает
источник
Существуют создатели синхронных действий, а затем - создатели асинхронных действий.
Создатель синхронного действия - это тот, который, когда мы вызываем его, немедленно возвращает объект Action со всеми соответствующими данными, прикрепленными к этому объекту, и готов к обработке нашими редукторами.
Создатели асинхронных действий - это те, в которых потребуется немного времени, прежде чем он будет готов в конечном итоге отправить действие.
По определению, каждый раз, когда у вас есть создатель действия, который отправляет сетевой запрос, он всегда будет считаться создателем асинхронного действия.
Если вы хотите, чтобы создатели асинхронных действий были внутри приложения Redux, вам нужно установить нечто, называемое промежуточным программным обеспечением, которое позволит вам иметь дело с этими создателями асинхронных действий.
Вы можете убедиться в этом в сообщении об ошибке, в котором говорится, что для асинхронных действий используется пользовательское промежуточное ПО.
Так что же такое промежуточное ПО и зачем оно нам нужно для асинхронного потока в Redux?
В контексте промежуточного программного обеспечения с избыточностью, такого как redux-thunk, промежуточное программное обеспечение помогает нам иметь дело с создателями асинхронных действий, поскольку это то, что Redux не может обработать из коробки.
С промежуточным программным обеспечением, интегрированным в цикл Redux, мы по-прежнему обращаемся к создателям действий, которые будут возвращать действие, которое будет отправлено, но теперь, когда мы отправляем действие, а не отправляем его непосредственно всем нашим редукторам, мы собираемся сказать, что действие будет отправлено через все различное промежуточное ПО внутри приложения.
Внутри одного приложения Redux у нас может быть столько промежуточного программного обеспечения, сколько мы хотим. По большей части, в проектах, над которыми мы работаем, у нас будет одно или два промежуточных ПО, подключенных к нашему магазину Redux.
Промежуточное программное обеспечение - это простая функция JavaScript, которая будет вызываться при каждом отправляемом нами действии. Внутри этой функции промежуточное ПО имеет возможность остановить отправку действия любому из редукторов, оно может изменить действие или просто поиграться с действием любым способом, который вы, например, могли бы создать, промежуточное ПО, которое регистрирует консоль каждое действие, которое вы отправляете, просто для вашего удовольствия.
Существует огромное количество промежуточного программного обеспечения с открытым исходным кодом, которое вы можете установить как зависимости в свой проект.
Вы не ограничены использованием только промежуточного программного обеспечения с открытым исходным кодом или установкой его в качестве зависимостей. Вы можете написать свое собственное промежуточное программное обеспечение и использовать его в своем магазине Redux.
Одно из наиболее популярных применений промежуточного программного обеспечения (и получение вашего ответа) предназначено для работы с создателями асинхронных действий, вероятно, наиболее популярным промежуточным программным обеспечением является redux-thunk, и оно помогает вам иметь дело с создателями асинхронных действий.
Существует много других типов промежуточного программного обеспечения, которые также помогают вам работать с создателями асинхронных действий.
источник
Чтобы ответить на вопрос:
Я бы сказал, по крайней мере, по двум причинам:
Первая причина - это разделение интересов, задача не
action creator
вызыватьapi
и возвращать данные, вам нужно передать два аргумента вашейaction creator function
,action type
иpayload
.Вторая причина заключается в том, что
redux store
объект ожидает простой объект с обязательным типом действия и, возможно, apayload
(но здесь вы также должны передать полезную нагрузку).Создатель действия должен быть простым объектом, как показано ниже:
И задание
Redux-Thunk midleware
поdispache
вашему,api call
чтобы результат соответствующийaction
.источник
При работе в корпоративном проекте в промежуточном программном обеспечении имеется много требований, таких как (saga), недоступных в простом асинхронном потоке, ниже приведены некоторые из них:
Список длинный, просто просмотрите расширенный раздел документации саги
источник