redux-наблюдаемый вы предоставили "undefined", где ожидался поток
Я использую fbsdk для получения данных пользователя в запросе ajax. Таким образом, имеет смысл сделать это в редукс-наблюдаемом эпосе. Как поступает запрос fbsdk, он не имеет .map()
и .catch()
он принимает успешные и отказоустойчивые обратные вызовы:
код:
export const fetchUserDetailsEpic: Epic<*, *, *> = (
action$: ActionsObservable<*>,
store
): Observable<CategoryAction> =>
action$.ofType(FETCH_USER_DETAILS).mergeMap(() => {
getDetails(store)
})
const getDetails = store => {
console.log(store)
let req = new GraphRequest(
'/me',
{
httpMethod: 'GET',
version: 'v2.5',
parameters: {
fields: {
string: 'email,first_name,last_name'
}
}
},
(err, res) => {
if (err) {
store.dispatch(fetchUserDetailsRejected(err))
} else {
store.dispatch(fetchUserDetailsFulfilled(res))
}
}
)
return new GraphRequestManager().addRequest(req).start()
}
Это дает ошибку:
TypeError: вы указали "undefined", где ожидался поток. Вы можете предоставить Observable, Promise, Array или Iterable.
Как вернуть наблюдаемый из эпоса, чтобы эта ошибка исчезла?
Попытка bindCallback
из этого ответа SO:
const getDetails = (callBack, details) => {
let req = new GraphRequest(
'/me',
{
httpMethod: 'GET',
version: 'v2.5',
parameters: {
fields: {
string: 'email,first_name,last_name'
}
}
},
callBack(details)
)
new GraphRequestManager().addRequest(req).start()
}
const someFunction = (options, cb) => {
if (typeof options === 'function') {
cb = options
options = null
}
getDetails(cb, null)
}
const getDetailsObservable = Observable.bindCallback(someFunction)
export const fetchUserDetailsEpic: Epic<*, *, *> = (
action$: ActionsObservable<*>
): Observable<CategoryAction> =>
action$.ofType(FETCH_USER_DETAILS).mergeMap(() => {
getDetailsObservable()
.mergeMap(details => {
return Observable.of(fetchUserDetailsFulfilled(details))
})
.catch(error => Observable.of(fetchUserDetailsRejected(error)))
})
Получение такой же ошибки
Ответы
Ответ 1
Взгляд в исходный код GraphRequestManager.start
:
start(timeout: ?number) {
const that = this;
const callback = (error, result, response) => {
if (response) {
that.requestCallbacks.forEach((innerCallback, index, array) => {
if (innerCallback) {
innerCallback(response[index][0], response[index][1]);
}
});
}
if (that.batchCallback) {
that.batchCallback(error, result);
}
};
NativeGraphRequestManager.start(this.requestBatch, timeout || 0, callback);
}
Как вы можете видеть, он ничего не возвращает, так эффективно undefined
. Для Rx mergeMap
требуется экземпляр Observable или что-то совместимое с ним (подробнее).
Поскольку вы отправляете дальнейшие действия, вы можете изменить свой исходный код следующим образом:
export const fetchUserDetailsEpic: Epic<*, *, *> = (
action$: ActionsObservable<*>,
store
): Observable<CategoryAction> =>
action$.ofType(FETCH_USER_DETAILS).do(() => { // .mergeMap changed to .do
getDetails(store)
})
const getDetails = store => {
console.log(store)
let req = new GraphRequest(
'/me',
{
httpMethod: 'GET',
version: 'v2.5',
parameters: {
fields: {
string: 'email,first_name,last_name'
}
}
},
(err, res) => {
if (err) {
store.dispatch(fetchUserDetailsRejected(err))
} else {
store.dispatch(fetchUserDetailsFulfilled(res))
}
}
)
return new GraphRequestManager().addRequest(req).start()
}
Честно говоря, я нахожу вашу вторую попытку бит лучше/менее связанной. Чтобы заставить его работать, вы можете сделать что-то вроде:
const getDetails = Observable.create((observer) => {
let req = new GraphRequest(
'/me',
{
httpMethod: 'GET',
version: 'v2.5',
parameters: {
fields: {
string: 'email,first_name,last_name'
}
}
},
(error, details) => {
if (error) {
observer.error(error)
} else {
observer.next(details)
observer.complete()
}
}
)
new GraphRequestManager().addRequest(req).start()
})
export const fetchUserDetailsEpic: Epic<*, *, *> = (
action$: ActionsObservable<*>
): Observable<CategoryAction> =>
action$.ofType(FETCH_USER_DETAILS).mergeMap(() => {
getDetails()
.map(details => fetchUserDetailsFulfilled(details)) // regular .map should be enough here
.catch(error => Observable.of(fetchUserDetailsRejected(error)))
})
Ответ 2
Похоже, что у @artur grzesiak есть правильный ответ, но для полноты это, как я думаю, можно использовать bindCallback
.
Единственная проблема, с которой я столкнулся с ответом Артура: я не думаю, что нам нужно поймать ошибку в эпосе, поскольку fetchUserDetailsRejected
- это действие обработки ошибок (предположительно, редуктор имеет дело с ним соответствующим образом).
Я использовал эту ссылку RxJs Static Public Methods: bindCallback
Дайте ему функцию f типа f (x, callback), и она вернет функцию g, которая при вызове g (x) выведет Observable.
// This callback receives the results and returns one or other action (non-observable)
const callback = (err, res) => {
return err
? fetchUserDetailsRejected(err)
: fetchUserDetailsFulfilled(res)
}
// Here is the graph request uncluttered by concerns about the epic
const getDetails = (store, callback) => {
console.log(store)
let req = new GraphRequest(
'/me',
{
httpMethod: 'GET',
version: 'v2.5',
parameters: {
fields: {
string: 'email,first_name,last_name'
}
}
},
callback
)
new GraphRequestManager().addRequest(req).start()
}
// This bound function wraps the action returned from callback in an Observable
const getDetails$ = Observable.bindCallback(getDetails).take(1)
// The epic definition using bound callback to return an Observable action
export const fetchUserDetailsEpic: Epic<*, *, *> =
(action$: ActionsObservable<*>, store): Observable<CategoryAction> =>
action$.ofType(FETCH_USER_DETAILS).mergeMap(() => getDetails$(store))
Ответ 3
Я не очень хорошо помню, как работала с RxJS >= 6
redux-observable
перед использованием RxJS >= 6
но я постараюсь помочь;)
Во-первых, вам не нужно отправлять себя, сокращающий-наблюдаемый сделает это за вас. В этой статье они показывают, как это работает под капотом, поэтому они называют отправку, но вам это не нужно. В новой реализации они удалили store
в качестве второго аргумента в пользу потока state
:
const epic = (action$, store) => { ... //before
const epic = (action$, state$) => { ... //after
Но самое главное, проблема, которую вы испытываете, заключается в том, что вы не возвращаете поток действий, а одно (отправленное) действие. На своем веб-сайте:
Это функция, которая принимает поток действий и возвращает поток действий.
Поэтому я считаю, что быстрым решением было бы вернуть наблюдаемые данные из вашего обратного вызова:
(err, res) => {
if (err) {
return Observable.of(fetchUserDetailsRejected(err))
}
return Observable.of(fetchUserDetailsFulfilled(res))
}
Я буду обновлять ответ на основе ваших комментариев. Удачи!
Ответ 4
Я считаю, что это возможная причина undefined
. Вы возвращаете undefined
в mergeMap
.
это
action$.ofType(FETCH_USER_DETAILS).mergeMap(() => {
getDetails(store)
})
должны быть либо
action$.ofType(FETCH_USER_DETAILS).mergeMap(() => getDetails(store))
или же
action$.ofType(FETCH_USER_DETAILS).mergeMap(() => {
return getDetails(store)
})