web-dev-qa-db-ja.com

redux-observableは、ストリームが予期されていた「未定義」を提供しました

Fbsdkを使用して、ajaxリクエストでユーザーの詳細を取得しています。したがって、これをreduxで観察可能な叙事詩で行うのは理にかなっています。 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を提供できます。

このエラーがなくなるように、エピックからオブザーバブルを返すにはどうすればよいですか?

これから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)))
  })

同じエラーを取得する

16

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

正直なところ、私はあなたの2回目の試みが少し良い/少ない結合を見つけます。動作させるには、次のようなことができます。

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)))
  })
4
artur grzesiak

@artur grzesiakには正しい答えがあるように見えますが、完全を期すためにbindCallbackを使用できると思います。

Arturの答えで私が抱えている唯一の問題は、fetchUserDetailsRejectedがエラー処理アクションであるため、エピックでエラーをキャッチする必要がないと思うことです(おそらく、reducerは適切に処理します)。

このリファレンスを使用しました RxJs Static Public Methods:bindCallback

タイプf(x、callback)の関数fを与えると、g(x)として呼び出されるとObservableを出力する関数gを返します。

// 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
Richard Matsen

どのように働いていたかよく覚えていないredux-observable使用前RxJS >= 6しかし、私は助けようとします;)

まず、自分自身を派遣する必要はありません。redux-observableが自動的に行います。 この記事では 、彼らはそれが内部でどのように機能するかを示しているので、彼らはディスパッチを呼び出しますが、あなたはする必要はありません。新しい実装では、storeストリームを優先して、2番目の引数として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))
}

コメントに基づいて回答を更新します。幸運を!

2
Sebabouche

これが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)
})
2
Dhirendra