redux-observable 您提供了 'undefined' 预期流的位置
redux-observable you provided 'undefined' where a stream was expected
我正在使用 fbsdk 在 ajax 请求中获取用户详细信息。所以在 redux-observable 史诗中这样做是有意义的。 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: You provided 'undefined' where a stream was expected. You
can provide an Observable, Promise, Array, or Iterable.
我如何return 从史诗中观察到这个错误消失?
尝试 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)))
})
出现同样的错误
我不太记得在使用 RxJS >= 6
之前 redux-observable
是如何工作的,但我会尽力提供帮助 ;)
首先,你不需要自己调度,redux-observable 会为你完成。 In this article,他们展示了它是如何在引擎盖下工作的,所以他们调用了 dispatch,但你不必这样做。在新的实现中,他们删除了 store
作为支持 state
流的第二个参数:
const epic = (action$, store) => { ... //before
const epic = (action$, state$) => { ... //after
但最重要的是,您遇到的问题是您没有return 动作流,而是单个(已分派的)动作。
From their website:
It is a function which takes a stream of actions and returns a stream of actions.
所以我认为一个快速的解决方案是 return 来自回调的可观察值:
(err, res) => {
if (err) {
return Observable.of(fetchUserDetailsRejected(err))
}
return Observable.of(fetchUserDetailsFulfilled(res))
}
我会根据您的评论更新答案。祝你好运!
我认为这似乎是 undefined
的可能原因。您将在 mergeMap
回调中返回 undefined
。
这个
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)
})
查看 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);
}
如您所见,它 return 什么也没做,如此有效 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)))
})
看起来@artur grzesiak 的答案是正确的,但为了完整起见,我认为 bindCallback
可以这样使用。
我对 Artur 的回答的唯一问题是我认为我们不需要捕捉史诗中的错误,因为 fetchUserDetailsRejected
是一个 error-handling 动作(大概是 reducer 处理它适当地)。
我用了这个参考RxJs Static Public Methods: bindCallback
Give it a function f of type f(x, callback) and it will return a function g that when called as g(x) will output an 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))
我正在使用 fbsdk 在 ajax 请求中获取用户详细信息。所以在 redux-observable 史诗中这样做是有意义的。 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: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
我如何return 从史诗中观察到这个错误消失?
尝试 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)))
})
出现同样的错误
我不太记得在使用 RxJS >= 6
之前 redux-observable
是如何工作的,但我会尽力提供帮助 ;)
首先,你不需要自己调度,redux-observable 会为你完成。 In this article,他们展示了它是如何在引擎盖下工作的,所以他们调用了 dispatch,但你不必这样做。在新的实现中,他们删除了 store
作为支持 state
流的第二个参数:
const epic = (action$, store) => { ... //before
const epic = (action$, state$) => { ... //after
但最重要的是,您遇到的问题是您没有return 动作流,而是单个(已分派的)动作。 From their website:
It is a function which takes a stream of actions and returns a stream of actions.
所以我认为一个快速的解决方案是 return 来自回调的可观察值:
(err, res) => {
if (err) {
return Observable.of(fetchUserDetailsRejected(err))
}
return Observable.of(fetchUserDetailsFulfilled(res))
}
我会根据您的评论更新答案。祝你好运!
我认为这似乎是 undefined
的可能原因。您将在 mergeMap
回调中返回 undefined
。
这个
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)
})
查看 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);
}
如您所见,它 return 什么也没做,如此有效 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)))
})
看起来@artur grzesiak 的答案是正确的,但为了完整起见,我认为 bindCallback
可以这样使用。
我对 Artur 的回答的唯一问题是我认为我们不需要捕捉史诗中的错误,因为 fetchUserDetailsRejected
是一个 error-handling 动作(大概是 reducer 处理它适当地)。
我用了这个参考RxJs Static Public Methods: bindCallback
Give it a function f of type f(x, callback) and it will return a function g that when called as g(x) will output an 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))