react-fetching-library で Suspense 使ってみた。


最近ReactでAPIからデータフェッチする際にuseEffectを使っていましたが、
これを読んだあと、これからはSuspenseを積極的に使っていこうと思い立ちました。
https://overreacted.io/ja/a-complete-guide-to-useeffect/

そんな自分にタイムリーなパッケージ、4日前(投稿日現在)に出来立てホヤホヤの
react-fetching-libraryを発見。
早速使ってみたいと思います!

https://marcin-piela.github.io/react-fetching-library/#/?id=usesuspensequery

Suspense使用前

ポートフォリオに投稿したQiitaの記事を載せるために、こんな感じで実装していました。
レンダリングしている内容は省略します🙇‍♂️

App.js
import React from 'react'
import Writing from 'sections/Writing'

export default () => {
  return (
    <div>
      <Writing />
    </div>
  )
}
sections/Writing.js

import React, { useState, useEffect } from 'react'
import { fetchQiitaArticles } from 'lib/api'

export default () => {
  const [articles, setArticles] = useState([])

  useEffect(() => {
    const handleGetArticles = response => {
      setArticles(response)
    }
    fetchQiitaArticles({ handleGetArticles })
  }, [])

  return (
    // fetchしたarticlesを使ってレンダリング
  )
}
lib/api.js
export const fetchQiitaArticles = async props => {
  const { handleGetArticles } = props
  await fetchGet({
    auth: process.env.REACT_APP_QIITA_AUTH,
    url: 'https://qiita.com/api/v2/authenticated_user/items',
    successAction: handleGetArticles,
  })
}

const fetchGet = async props => {
  const { auth, url, successAction, failureAction } = props

  return await fetch(url, {
    headers: {
      Authorization: `Bearer ${auth}`,
    },
  })
    .then(response => response.json())
    .then(responseJson => {
      // console.log(responseJson)
      if (successAction) {
        successAction(responseJson)
      }
    })
    .catch(error => {
      // console.error(error)
      if (failureAction) {
        failureAction()
      }
    })
}

セットアップ

まずは react-fetching-libraryをインストールします。


$ yarn add react-fetching-library

続いてClientの作成します。
とりあえず今回はオプションなし。

api/Client.js
import { createClient } from 'react-fetching-library'
export const Client = createClient()

そして<ClientContextProvider>でAppを囲います。

App.js
import React from 'react'
import { ClientContextProvider } from 'react-fetching-library'
import { Client } from 'api/Client'
import Writing from 'sections/Writing'

export default () => {
  return (
    <ClientContextProvider client={Client}>
      <Writing />
    </ClientContextProvider>
  )
}

これで、react-fetching-libraryの機能が使用可能になりました!

fetchの用意

react-fetching-libraryで使用するGETメソッドを作成します。

api/fetchMyQiitaArticles.js
export const fetchMyQiitaArticles = {
  method: 'GET',
  endpoint: 'https://qiita.com/api/v2/authenticated_user/items',
  headers: {
    Authorization: `Bearer ${process.env.REACT_APP_QIITA_AUTH}`,
  },
}

コンポーネントのラップ

useEffectを廃止してWritingContainerでラップします。
その際、useSuspenseQueryを使ってデータフェッチを行うようにします。

container/WiritingContainer.js
import React from 'react'
import { useSuspenseQuery } from 'react-fetching-library'
import { fetchMyQiitaArticles } from 'api/fetchMyQiitaArticles'
import Writing from 'sections/Writing'

export default () => {
  const { payload, error, query } = useSuspenseQuery(fetchMyQiitaArticles)

  return <Writing error={error} articles={payload} />
}
sections/Writing.js
import React, { useState, useEffect } from 'react'
import { fetchQiitaArticles } from 'lib/api'

export default props => {
  const { articles, error } = props

  return (
    // fetchしたarticlesを使ってレンダリング
  )
}

Suspenseを使う!

App.js
import React, { Suspense } from 'react'
import { ClientContextProvider } from 'react-fetching-library'
import { Client } from 'api/Client'
import WritingContainer from 'container/WritingContainer'

export default () => {
  return (
    <ClientContextProvider client={Client}>
      <Suspense fallback={<p>Loading ...</p>}>
        <WritingContainer />
      </Suspense>
    </ClientContextProvider>
  )
}

かなりアバウトになりましたが、
これでSuspenseを使ったデータフェッチができました 🎉

おわりに

とりあえず自分はuseEffectを使わずにデータフェッチができるようになりました。
Suspense界隈のリリース情報はしっかり追っていきたいですね。
あとreact-fetching-libraryの公式DocがTypeScriptで説明されていたので、
TypeScriptでの開発のモチベーションになりそうです。

参考