DEV Community

Discussion on: Data fetching React Hook

Collapse
 
devhammed profile image
Hammed Oyedele

Nice article but you should look into AbortController for that subscribed part as this would also cancel the actual request if it is ongoing:

import * as React from "react"

export type RequestState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; error: Error }

export type RequestAction<T> =
  | { type: "start" }
  | { type: "completed"; data: T }
  | { type: "failed"; error: Error }

export function useFetch<T>(route: string): RequestState<T> {
  const [state, dispatch] = React.useReducer<
    React.Reducer<RequestState<T>, RequestAction<T>>
  >(reducer, { status: "idle" })
  React.useEffect(() => {
    const abortController = new AbortController()

    if (route) {
      dispatch({ type: "start" })
      fetch(route, { signal: abortController.signal })
        .then(response => {
          if (!response.ok) {
            throw new Error("Request failed")
          }
          return response.json()
        })
        .then(data => {
            dispatch({ type: "completed", data })
        })
        .catch(error => {
          if (error.name !== 'AbortError') {
            dispatch({ type: "failed", error })
          }
        })
    }
    return () => {
      abortController.abort()
    }
  }, [route])
  return state
}

export function reducer<T>(
  state: RequestState<T>,
  action: RequestAction<T>
): RequestState<T> {
  switch (action.type) {
    case "start":
      return {
        status: "loading",
      }
    case "completed":
      return {
        status: "success",
        data: action.data,
      }
    case "failed":
      return {
        status: "error",
        error: action.error,
      }
    default:
      return state
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mbellagamba profile image
Mirco Bellagamba

Thanks for the suggestion! This AbortController solution makes more explicit the cancel operation. Anyways, I don't use it very often because of the low browser support. Maybe, I should start using it shipping a polyfill for older browsers 🤔.

Collapse
 
devhammed profile image
Hammed Oyedele

If you are worried about browser support, axios offers a similar API on top of XHR or go the polyfill way as you have said.

Collapse
 
sebalreves profile image
sebalreves

how can i use this hook for submitting a form? it gives me an error because it's calling inside the submitHandler function. Or it's better just to write an asynchronous function for this?

Collapse
 
mbellagamba profile image
Mirco Bellagamba

This hook cannot work for mutation requests, like form submissions, because it starts the fetch request as soon as the component is mounted. If you need to handle form submissions you should write a "classic" async handler.