💡

【JS / TS】非同期通信(axios)ごとにtry~catchを実装したくない人のためのエラーハンドリング Tips💡

に公開
1

Discussion

nap5nap5

neverthrowから提供されているResult型などを使って実装にチャレンジしてみました。

本記事で達成したい"try~catchを実装したくない"というコンセプトはBFF以外で達成できたのではないかと思います。

デモコードです。

https://codesandbox.io/p/sandbox/hopeful-snyder-z0sjg1?file=%2Fsrc%2Ffeatures%2Fping%2Frepository%2Findex.ts&selection=[{"endColumn"%3A2%2C"endLineNumber"%3A24%2C"startColumn"%3A1%2C"startLineNumber"%3A9}]

デモコードの抜粋です。

レポジトリ側

async function requestToBFF(): Promise<ServerSideEnvData> {
  const response: AxiosResponse<ServerSideEnvData, ErrorData> = await axios.get(
    '/api/ping'
  )
  const { data } = response
  return data
}

export class ServerSideEnvRepository implements ServerSideEnvFactory {
  async ping(): Promise<Result<ServerSideEnvData, ErrorData>> {
    return ResultAsync.fromPromise<ServerSideEnvData, ErrorData>(
      requestToBFF(),
      (e) => e as ErrorData
    ).map((value) => value)
  }
}

フック側

const usePingHook = () => {
  // https://stackoverflow.com/a/63113066/15972569
  const { data, error, refetch } = useQuery<ServerSideEnvData, ErrorData>(
    [SERVER_SIDE_ENV_KEY],
    async () => {
      const result = await pingRepository.ping()
      if (result.isErr()) {
        return Promise.reject(result.error)
      }
      return result.value
    },
    {
      onSuccess: function (data) {},
      onError: function (error) {},
      onSettled: function (data, error) {},
    }
  )
  return { data, error, refetch }
}

export default usePingHook

コンポーネント側

const Ping = () => {
  const { data, error, refetch } = usePingHook()

  if (error) {
    return (
      <PingLayout>
        <button
          onClick={() => {
            queryClient.removeQueries([SERVER_SIDE_ENV_KEY])
            refetch()
          }}
        >
          Refetch
        </button>
        <ShowMe data={error} />
      </PingLayout>
    )
  }

  if (!data) {
    return (
      <PingLayout>
        <p>{`loading...`}</p>
      </PingLayout>
    )
  }

  return (
    <PingLayout>
      <button
        onClick={() => {
          queryClient.removeQueries([SERVER_SIDE_ENV_KEY])
          refetch()
        }}
      >
        Latest Refresh
      </button>
      <ShowMe data={data} />
    </PingLayout>
  )
}

export default Ping

簡単ですが、以上です。