rxjsを触ってみたくて、redux observableとgraphql(apollo client)を使った簡単な画面を作りました。
ポケモンの名前でgif画像の検索ができます。
github
https://github.com/pokotyan/next-observable
デプロイ先
https://flamboyant-shaw-acfdd8.netlify.com/
利用した外部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してね、っていう感じみたいです。