React Hooks でいい感じに fetch する

fetch を React Hooks 化したいと思って実装をいくつか探してみたのですが、良さげなものが無かったので自分で作ってみました。

特徴としては、

  • 戻り値に型を付けるのが前提の呼び出しになっている
  • useFetch 時点では実際の fetch を呼ばず、戻り値として返ってきた関数を使って初めて fetch が呼ばれるため、呼ぶタイミングを制御できる
  • GET 操作でも POST 操作でも useFetch が使える
  • POST 操作で戻り値が常に空である場合でも、リクエストの完了を契機に別の処理を走らせられる

簡単な使い方はこんな感じです。

まずは準備です。最初に API から取得するレスポンスの型 Personと、レスポンスからその型の値に変換する関数 resolvePerson を定義しています。

次に、エラーはコールバックでやってくるので onError 関数を定義しています。引数は string 型ですが、実際はもう少し汎用的に使えるエラー型にします。

これで準備が出来たので useFetch 関数を呼び出します。Blog か Text か JSON か FormData かで利用する関数が異なっていて、JSON を扱う場合は useFetchJSON を利用します。

useFetchJSON の引数は fetch 関数と同じような感じで、URL とオプションを入れます。3番目の引数には取得した AnyJson 型のデータを T 型に変換する関数を渡します。ここでは resolvePerson を渡しています。

useFetchJSON の戻り値として[T | null, number, ((url: string | null, opts: RequestInit) => void)] 型の3要素のタプルが返されるようになっていて、最初の要素がレスポンスの値、2番目がリクエストが成功した時にインクリメントされるカウンター、最後が実際に fetch する関数、となっています。

fetch する関数を呼ばない限り、実際のリクエストは発生しません。なので、発生する可能性のあるリクエストをとりあえず useFetchJSON しておき、必要に応じてそれを呼ぶみたいな感じになります。この例では useEffect を使うことで初回のみ fetch しています。

実際にレスポンスが取得できたら personResp が非 null になります。それまでは null です。

GET と POST を混ぜた例はこんな感じになります。

POST 操作が完了すると、 useFetchJSON の戻り値タプルの2番目に入っているカウンターがインクリメントされます。そのため、これを useEffect などの dependencies に含めることで、リクエストの完了を契機に別の処理を実行できるようになっています。ここでは POST 操作が完了したら再度 GET を走らせています。

注意点としては、1レンダリングの間に複数回 fetch する関数を呼んでも1回しか実行されないこと、POST が完了する前に再度 POST を実行した場合は直前のリクエストが abort されて再度呼んでしまうことがあります。

合同会社Wandboxの代表社員

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store