Reactデータ取得&ステート管理ライブラリSWRのオレオレラッパーを作ってみた

# TL;DR

### SWR wrapper
```typescript:useCustomeSWR.ts
import useSWR, { BareFetcher } from 'swr'
import { PublicConfiguration } from 'swr/dist/types'

import { buildApiClient } from '@/lib/apiClient'

type AxiosOptions = {
  baseUrl?: string
}

const useCustomSWR = <Data>(
  key: string,
  bareFetcher: BareFetcher<Data> | undefined | null = undefined,
  config: Partial<PublicConfiguration<Data, any, BareFetcher<Data>>> | undefined = undefined,
  axiosOptions: AxiosOptions = {},
) => {
  const fetcher = getFetcher<Data>(bareFetcher, axiosOptions)
  return useSWR<Data>(key, fetcher, config)
}

const getFetcher = <Data>(
  fetcher: BareFetcher<Data> | null | undefined,
  axiosOptions: AxiosOptions = {},
) => {
  if (fetcher === undefined) {
    const apiClient = buildApiClient(axiosOptions)
    return (url: string) => apiClient.get(url).then((r) => r.data)
  }

  return fetcher
}

export default useCustomSWR

```

### Axios wrapper

```typescript:apiClient.ts
import axios from 'axios'

type Props = {
  baseUrl?: string
  headers?: {}
}

export const buildApiClient = ({ baseUrl = 'http://localhost:8080', headers = {} }: Props) => {
  const customHeaders = { ...headers, ...{ 'Content-Type': 'application/json' } }

  const apiClient = axios.create({
    baseURL: baseUrl,
    responseType: 'json',
    headers: customHeaders,
  })

  apiClient.interceptors.request.use(
    (config) => {
      console.log(config)
      return config
    },
    (error) => {
      console.error(error)
      return Promise.reject(error)
    },
  )

  apiClient.interceptors.response.use(
    (response) => {
      console.log(response)
      return response
    },
    (error) => {
      switch (error.response?.status) {
        case 400:
        case 401:
          break
        default:
          console.error(error)
      }
    },
  )

  return apiClient
}

```

# ざっくりSWRとは

データ取得のための React Hooks ライブラリです。
以下のような特徴があり、シンプルかつ高機能なデータ取得から取得データのステート管理までをまるっと行ってくれます。
ref: [swr.vercel.app](https://swr.vercel.app/ja)
>- 速い、 軽量 そして 再利用可能 なデータの取得
>- 組み込みの キャッシュ とリクエストの重複排除
>- リアルタイム な体験
>- トランスポートとプロトコルにとらわれない
>- SSR / ISR / SSG support
>- TypeScript 対応
>- React Native




SWRにはglobal configが提供されており、こちらを使用することで`useSWR`の処理をカスタマイズすることができますが呼び出し元によって設定をカスタマイズしたいユースケースを考えwrapperを作成し利便性を上げてみました。

ref: [swr.vercel.app グローバルな設定](https://swr.vercel.app/ja/docs/global-configuration)

# 使い方

### データ取得で使う
　
```typescript:useFetchData.ts
import useCustomSWR from '@/lib/useCustomSWR'

type Data = {
  data: { code: number; name: string }[]
}

const useFetchData = () => {
  const { data, error } = useCustomSWR<Data>('/api_endpoint')

  return {
    data,
    isLoading: !error && !data,
    isError: error,
  }
}

export default useFetchData
```

`<Data>`のようにtypeを渡すことで返却される `data` のtypeを定義できるのがポイントです。



### APIリクエストしないステート管理で用いる

SWRは第二引数のfetcherにnullを渡すとAPIリクエストをしないようにできるのがポイントです。これとSWRによって提供されている`mutate`という関数を用いてグローバルステート管理に使えます。
ref: [SWRはローカルの状態管理としても使える](https://zenn.dev/tak_iwamoto/articles/39aefec675c323)

```typescript:useGlobalNotification.ts
import useCustomSWR from '@/lib/useCustomSWR'

const useGlobalNotification= () => {
  const { data: message, mutate: setMessage } = useCustomSWR<string>(
    'notification',
    null,
    {
      fallbackData: "",
    },
  )
  return {
    message,
    setMessage
  }
}

export default useGlobalNotification
```
一つ目の引数にはglobalStateのkeyとなる文字列を渡し、二つ目のfetcherにnull、三つ目のoptionsにはfallbackDataを渡すことによりデフォルトのステートの値を定義できます。
keyはユニークでないといけないので実際にはenumなどを用いて一つのファイルで定義しておくと良いでしょう。

こちらを各コンポーネントで呼びsetMessageを用いてmessageステートを変更することでmessageステートの参照コンポーネントにレンダリングが走ります。

# まとめ
Reactのステート管理ツールには他にもrecoil, react-queryなどがありますが個人的にはSWRがシンプルかつパワフルな機能を提供していてオススメです！試したことない方は是非使ってみてください！