LoginSignup
1
1

More than 5 years have passed since last update.

redux observableとgraphqlを組み合わせてみる

Last updated at Posted at 2018-09-23

rxjsを触ってみたくて、redux observableとgraphql(apollo client)を使った簡単な画面を作りました。

ポケモンの名前でgif画像の検索ができます。
github
https://github.com/pokotyan/next-observable
デプロイ先
https://flamboyant-shaw-acfdd8.netlify.com/

スクリーンショット 2018-09-23 20.17.34.png

利用した外部API、データ

https://qiita.com/seya/items/47dc0ebae55674d8902f
こちらで紹介されていたものを利用しました。具体的には以下の3つを利用しています。

https://github.com/lucasbento/graphql-pokemon
https://github.com/fanzeyi/Pokemon-DB
http://www.pokestadium.com/tools/sprites

rxjs

まだ私も勉強中ですが、ざっくりいうとObservable(川)にストリーム(桃)を流して、rxjsのオペレータでその桃を切り刻んだり、なんやかんやしたりします。

redux observableだとactionの発火をトリガーに桃が流れ始めるイメージです。
あるactionの発火で桃が流れ始め、桃に対してドメインの処理を行い、また別のactionを返します。
これをredux observableではepicsと言うようです。
以下は、ポケモンの一覧データを取得するepicです。

export const fetchPokemons = (action$) =>
  action$.pipe(
    ofType(pokemonActions.FETCH_POKEMONS), // 1)FETCH_POKEMONSが発火したら
    mergeMap(action => client.query({      // 2)graphqlのクエリを叩いて
      variables: {
        first: action.payload.first,
      },
      query: gql`
        query($first: Int!) {
          pokemons(first: $first) {
            id
            number
            name
            attacks {
              special {
                name
                type
                damage
              }
            }
            evolutions {
              id
              number
              name
              weight {
                minimum
                maximum
              }
              attacks {
                fast {
                  name
                  type
                  damage
                }
              }
            }
          }
        }
      `
    })),
    map(response => { // 3) graphqlのレスポンスに日本語のポケモン名を足して
      return response.data.pokemons.map(pokemon => {
        let jname;

        pokemonConfigList.some(pokemonConfig => {
          if (pokemonConfig.ename === pokemon.name) {
            jname = pokemonConfig.jname;
            return true;
          }
        })

        pokemon.jname = jname || '';

        return pokemon;
      })
    }),
    map(pokemons => { // 4) graphqlのレスポンスにgifのurlを足して
      return pokemons.map(pokemon => {

        const pokemonGifUrl = pokemon.name.toLowerCase()
          .replace(/\./g,'')
          .replace(/'/g,'')
          .replace(/\-/g, '')
          .replace(/\s/g, '-');

        pokemon.gif = `http://www.pokestadium.com/sprites/xy/${pokemonGifUrl}.gif`;

        return pokemon;
      })
    }),
    map(pokemons => pokemonActions.fetchPokemonsFulfilled(pokemons)) // 5) actionをdispatchする
  );

あるactionを受けて桃が流れ始め、最終的には別のactionが返されるという流れが基本となっているため、以下のように同じactionを返すepicを作ると無限ループが発生してしまします。

// DO NOT DO THIS
const actionEpic = action$ => action$; // creates infinite loop

参考:https://redux-observable.js.org/docs/basics/Epics.html

redux observableの便利に感じたところ

ボタンなどのクリックをトリガーにリクエストを投げる際、そのままだと、ボタン押下のたびにリクエストが走ります。
redux observableを使えば、ある一定間隔内に連続して投げられた処理は1回の処理として間引く。ということが簡単にできます。


export const fetchPokemons = (action$) =>
  action$.pipe(
    ofType(pokemonActions.FETCH_POKEMON),
    debounceTime(500), // 500ミリ秒の間はFETCH_POKEMONが何度発火しようが、処理は1回にまとめられる
    // some code
  );

これ以外にも、前回と値が違う時のみ処理を通すなど、rxjsには色々なオペレータがあります。
フォームの入力に伴ういろんな処理のハンドリングに便利なものがrxjsには備わっているように感じました。

redux observableとapollo clientの組み合わせ方

通常apollo clientを使う場合、ApolloProviderを使ってgraphqlのクライアントを全体に流し込みますが、redux observableと組み合わせる場合、graphqlを利用したい場所で都度クライアントをimportしてね、っていう感じみたいです。

rxjsの学習に使えるサイト

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1