はじめに
React Advent Calendar 2019 8日目の記事で、担当の@Yamashouです!
今回はフロントエンド初心者なりに、Web開発していて、このライブラリいいんじゃない?と思っていたけど、なかなか時間が取れずさわれていなかったものをこの場を借りてその記録を残そうと思います!
背景
Webのフロントの開発はこれまでほとんどやったことはなかったのですが、React+Typescriptで状態管理はReduxを使って開発を今年の春から夏にかけてやっていたのですが、その時にずっと思っていたのが、Reduxに関して思うところがちょくちょくありました。
『コンポーネントは分けれても、状態やロジックがどうしても依存してしまうな』
『hooksに徐々に変えてもやはり外部のapiやfirebaseとのやりとりで悩む部分があるな〜』
とか思ってました。
(これはもしかしたら知見が足りないだけかもしれないですが、、、、、、、🙇♂️)
そこで、状態管理をシンプルにしたくて、hooksでやる方法とかを調べてそれに置き換えたりもしました!
そして、useFetchを実装しようかなと考えています!
しかし、その時の選択肢の一つとして、RxJSを採用するというのが僕の中ではよぎったけれど、学習コストも高そうだし、
そもそも新しい概念を入れたら、Reduxよりも複雑になってしまうんじゃないか?というふうに思い僕の中でそっと却下しました。
そこで、今回はアドベントカレンダーということで、hooksとRxJSを使用することで、Viewとロジックとステートをうまく分離できたりしないかな〜と思って、hooksで扱えないかなと思ってます!
選択肢
今回RxJSを使用するにあって選択肢は以下のよう
- RxJS単体で導入する
- redux-obserbavleを導入する
今回はhooksと絡めて、採用したいのでそれぞれhooksがあるかどうかを考えていきます
RxJSでhooksでは以下のような選択肢があります!!
- 既存のhooksを組み合わせて、実装する
- rxjs-hooksを採用する
今回はrxjs-hooksを採用した場合について考えていくことにします
rxjs-hooksとは
RxJSをReact hooksとして使用するためのライブラリの一つで、いくつかあるライブラリの中で一番開発がされていると思われたライブラリです
使用方法
基本的には二つのhooksを用意されています!
- useObservable
- useEventCallback
調べていたら、すでに試されている方がいらっしゃったので、こちらを参考にGoogle news apiを叩くhooksをrxjs-hooksで実装してみます
参考: rxjs-hooksで楽しむReact HooksとRxJS
export interface Source {
id?: any;
name: string;
}
export interface Article {
source: Source;
author: string;
title: string;
description: string;
url: string;
urlToImage: string;
publishedAt: Date;
content: string;
}
export interface RootObject {
status: string;
totalResults: number;
articles: Article[];
}
const newsApiKey = "Google new api key";
const newsApiEndpoint = "https://newsapi.org/v2/top-headlines";
export const getNewsFromWord = async (word: string):Promise<Article[]> => {
const { data } = await axios.get<RootObject>(newsApiEndpoint,{
params: {
country: "jp",
q: word,
sortBy: "publishedAt",
apiKey: newsApiKey,
}
});
return data.articles
};
// useObservableを使用したパターン
export const useGetNewsFromWord = (word: string): Article[] => {
return useObservable<Article[], string[]>(
(word$) =>
word$.pipe(
debounceTime(500),
map(([val]) => val),
switchMap(getNewsFromWord),
catchError(err => {
throw err;
})
),
[],
[word]
);
};
記事より、単純入力する場合は、useObservableが良いとのことでしたので、Google news apiで検索して、Top20記事を取得するhooksを作成してみました。とても単純な実装で、受けたword$を0.5秒まちその最初の要素をgetNewsFromWordに渡して、その結果を流しています。
良いと思った点
処理というか、Observerがコード的に綺麗に並んでいるので、それぞれが何をしているのかさえ理解できれば、「この後これが起きて、、、、」みたいなのが、とてもみとうしがいいと感じました。そして、StateとInputs引数の型を指定しているので、出口と入り口さえ方がわかればあとはそれを内部のObserverもそれに準拠されるので、間違った使い方をしていたりするとすぐに怒ってくれるので、とてもわかりやすいと感じました!(これはRxというよりはhooksのメリットでもあると思います)
苦労した点
正直useObservableのこの実装をTSで実現するににとても手間取ってしまいました。本家にはいくつかのexampleがあるのですが、型を定義した例がなくどういう型のものがどう渡ってるのかが想像つかなかってのでかなり型チェックで怒られました😅
word$.pipeの中はずっと型が違うと怒られて一旦全部型を書き出したり、RxJS公式API Listと睨めっこしながら、こういう時にはこれ!っていう知識がないので、「これを使えばいいのかな?」と恐る恐るつかってみてはエラーでやり直しの繰り返しでした、、、
まとめ
今回rxjs-hooksをRxJSそのものを学びつつ使ってみたものの、やはりそもそものRxJSに学習コストがかかる、
そして、hooksとして、View側から見ればかなり綺麗に見えるし、useObservableの方は結構すっきり書けると思いましたが、これはまだ単純な例ですし、ローディングなどを実装しようとなれば、
ただ、まだまだ、RxJSそのものの知見(Rxに対する知見も)とrxjs-hooksを使用せずに書いたことがないのし、今回は単純な例だったので、userFetchなどを実装して使用した方が、シンプルだと感じましたが、もう少し複雑なことやをRxJSを駆使して実装してみたりと、いじってみたいと思います!!