可观察的 redux-observable,您在预期流的位置提供了"未定义"



我正在使用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()
}

它给出错误:

类型错误:您在需要流的位置提供了"未定义"。你 可以提供可观察、承诺、数组或可迭代。

如何从史诗中返回可观察量,以便此错误消失?

尝试从这个 SO 答案中bindCallback

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)))
})

收到相同的错误

查看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。RxmergeMap需要可观察的实例或与之兼容的内容(更多信息(。

由于您调度了进一步的操作,因此您可以像这样修改原始代码:

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)))
})

我不太记得在使用RxJS >= 6之前redux-observable是如何工作的,但我会尽力帮助;)

首先,你不需要自己调度,redux-observable会为你做这件事。在本文中,他们展示了它在引擎盖下的工作原理,因此他们调用调度,但您不必这样做。在新的实现中,他们删除了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))
}

我将根据您的评论更新答案。祝你好运!

我相信这似乎是undefined的可能原因。您将在回调中返回undefinedmergeMap

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)
})

看起来 @artur grzesiak 有一个正确的答案,但为了完整起见,这就是我认为可以使用bindCallback的方式。

我对 Artur 的回答唯一的问题是我认为我们不需要捕捉史诗中的错误,因为fetchUserDetailsRejected是一个错误处理操作(大概化简器会适当地处理它(。

我使用了这个参考 RxJs 静态公共方法:bindCallback

给它一个类型为 f(x,

回调( 的函数 f,它将返回一个函数 g,当调用为 g(x( 时将输出一个可观察量。

// 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))

最新更新