Reactでタイムアウト時とアンマウント時にfetchをAbortするcustom hooks

実際は両者でエラーのクラスを分けたが、だいたいこんな感じ。

import { useRef, useEffect } from 'react'

export default function useFetch() {
  const abortsRef = useRef({})
  
  function fetchFoo() {
    return requestWithAbort(signal => fetch('/foo', { signal }))
  }

  function fetchBar() {
    return requestWithAbort(signal => fetch('/bar', { signal }))
  }
  
  function requestWithAbort(request) {
    const abortController = new AbortController()

    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        abortsRef.current[timeout].abort()
        delete abortsRef.current[timeout]
      }, 1000 * 60)

      abortsRef.current[timeout] = abortController

      request(abortController.signal).then(r => {
        resolve(r)
      }).catch(e => {
        reject(e)
      }).finally(() => {
        clearTimeout(timeout)
        delete abortsRef.current[timeout]
      })
    })
  }
  
  useEffect(() => {
    return () => {
      Object.values(abortsRef.current).forEach(c => c.abort())
    }
  }, [])
  
  return { fetchFoo, fetchBar }
}