Проблема в том, что внутри вашего switchMap
вы возвращаете обещание, которое само разрешается в concat Observable; Promise<ConcatObservable>
. оператор switchMap
прослушает обещание, а затем выдаст ConcatObservable как есть, который затем будет предоставлен store.dispatch
под капотом с помощью redux-observable. rootEpic(action$, store).subscribe(store.dispatch)
. Поскольку отправка Observable не имеет смысла, поэтому вы получаете сообщение об ошибке, что действия должны быть простыми объектами.
То, что выдает ваш эпик, всегда должно быть простыми старыми объектами действий JavaScript, т. е. { type: string }
(если у вас нет дополнительного промежуточного программного обеспечения для обработки других вещей)
Поскольку промисы всегда генерируют только одно значение, мы не можем использовать их для генерирования двух действий, нам нужно использовать Observables. Итак, давайте сначала преобразуем наш Promise в Observable, с которым мы можем работать:
const response = db.collection('categories')
.doc()
.set({
name: action.payload.name,
uid: user.uid
})
// wrap the Promise as an Observable
const result = Observable.from(response)
Теперь нам нужно отобразить этот Observable, который будет выдавать одно значение, на несколько действий. Оператор map
не выполняет операции "один ко многим", вместо этого мы будем использовать один из mergeMap
, switchMap
, concatMap
или exhaustMap
. В этом очень конкретном случае, какой из них мы выберем, не имеет значения, потому что Observable, к которому мы его применяем (который обертывает промис), будет когда-либо выдавать только одно значение, а затем будет завершено (). Тем не менее, очень важно понимать разницу между этими операторами, поэтому обязательно уделите некоторое время их изучению.
Я буду использовать mergeMap
(опять же, в этом конкретном случае это не имеет значения). Поскольку mergeMap
ожидает, что мы вернем «поток» (наблюдаемый объект, обещание, итератор или массив), я собираюсь использовать Observable.of
для создания наблюдаемого из двух действий, которые мы хотим сгенерировать.
Observable.from(response)
.mergeMap(() => Observable.of(
{ type: ADD_CATEGORY_SUCCESS },
{ type: GET_CATEGORIES_REQUEST }
))
Эти два действия будут запускаться синхронно и последовательно в том порядке, в котором я их указал.
Нам также нужно добавить обратную обработку ошибок, поэтому мы будем использовать оператор catch
из RxJS — различия между ним и методом catch
в промисе важны, но выходят за рамки этого вопроса.
Observable.from(response)
.mergeMap(() => Observable.of(
{ type: ADD_CATEGORY_SUCCESS },
{ type: GET_CATEGORIES_REQUEST }
))
.catch(err => Observable.of(
{ type: ADD_CATEGORY_ERROR, payload: err }
))
Соединяем все вместе, и у нас будет что-то вроде этого:
const addCategoryEpic = action$ => {
return action$.ofType(ADD_CATEGORY_REQUEST)
.switchMap((action) => {
const db = firebase.firestore()
const user = firebase.auth().currentUser
const response = db.collection('categories')
.doc()
.set({
name: action.payload.name,
uid: user.uid
})
return Observable.from(response)
.mergeMap(() => Observable.of(
{ type: ADD_CATEGORY_SUCCESS },
{ type: GET_CATEGORIES_REQUEST }
))
.catch(err => Observable.of(
{ type: ADD_CATEGORY_ERROR, payload: err }
))
})
}
Хотя это работает и отвечает на ваш вопрос, несколько редукторов могут вносить изменения в состояние из одного и того же единственного действия, что чаще всего следует делать вместо этого. Последовательное выполнение двух действий обычно является антишаблоном.
Тем не менее, как это часто бывает в программировании, это не абсолютное правило. Определенно бывают случаи, когда имеет смысл иметь отдельные действия, но это исключение. Вы лучше знаете, является ли это одним из тех исключительных случаев или нет. Просто имейте это в виду.
person
jayphelps
schedule
27.12.2017