【问题标题】:redux-observable you provided 'undefined' where a stream was expectedredux-observable 您在预期流的地方提供了“未定义”
【发布时间】:2018-10-03 04:40:55
【问题描述】:

我正在使用 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:您在预期流的位置提供了“未定义”。你 可以提供 Observable、Promise、Array 或 Iterable。

如何从史诗中返回一个 observable 以使这个错误消失?

从这个SO answer尝试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)))
  })

遇到同样的错误

【问题讨论】:

    标签: react-native react-redux redux-observable react-native-fbsdk


    【解决方案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 实例或与之兼容的东西 (more info)。

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

    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】:

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

      首先,您不需要自己派发,redux-observable 会为您完成。 In this article,他们展示了它在后台是如何工作的,所以他们称之为调度,但你不必这样做。在新的实现中,他们删除了 store 作为第二个参数以支持 state 流:

      const epic = (action$, store) => { ... //before
      const epic = (action$, state$) => { ... //after
      

      但最重要的是,您遇到的问题是您没有返回操作流,而是返回单个(分派的)操作。 From their website:

      它是一个接受动作流并返回动作流的函数。

      所以我认为一个快速的解决方案是从你的回调中返回 observables:

      (err, res) => {
        if (err) {
          return Observable.of(fetchUserDetailsRejected(err))
        }
        return Observable.of(fetchUserDetailsFulfilled(res))
      }
      

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

      【讨论】:

      • 谢谢,我得到了同样的错误,因为它需要很长时间才能找到返回这些 observables 的代码,并且它会立即返回 undefined
      • 不应与完成请求所需的时间相关联。我通常使用 rxjs ajax 进行 API 调用(或者在使用 fromPromise 转换 Axios 承诺之前),所以我从未处理过回调。你检查过这个SO question吗?好像和你的问题有关。
      【解决方案3】:

      我相信这似乎是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)
      })
      

      【讨论】:

      • 该回调返回undefined。无论如何,我现在明白了这个问题。尝试将 getDetails 函数包装在 Promise 中,在收到响应时解决/拒绝它。
      【解决方案4】:

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

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

      我使用了这个参考RxJs Static Public Methods: bindCallback

      给它一个 f(x, callback) 类型的函数 f,它会返回一个函数 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))
      

      【讨论】:

        猜你喜欢
        • 2023-03-23
        • 1970-01-01
        • 2021-12-10
        • 2020-03-10
        • 2018-09-07
        • 2019-08-08
        • 2020-03-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多