I am trying to make a generic useAxios hook in React. I would like to be able to import this hook into other components to make Get, Post, and Delete requests. I have created the hook and it works fine for making Get requests, but I am stuck on how to make it work for Post/Delete requests.
The issue is that I would be making the Post/Delete request when a user clicks a Save or Delete button, but I cannot call a React hook from an event handler function or from useEffect.
Below is the generic hook I created:
import { useState, useEffect } from "react"; import axios from "axios"; export interface AxiosConfig<D> { method?: 'get' | 'post' | 'delete' | 'put'; url: string; data?: D; params?: URLSearchParams; } export const useAxios = <T, D = undefined >(config: AxiosConfig<D>) => { const [responseData, setResponseData] = useState<T>(); const [isLoading, setIsloading] = useState(true); const [isError, setIsError] = useState(false); useEffect(() => { const controller = new AbortController(); const axiosRequest = async () => { try { const response = await axios({ ...config, signal: controller.signal }) setResponseData(response.data) setIsloading(false); } catch (error) { setIsError(true); setIsloading(false); } } axiosRequest(); return () => { controller.abort(); } }, [config.url, config.method, config.data, config.params]) return {responseData, isLoading, isError} }
And this is an example of a component where I would like to make a Delete request
import { useParams } from 'react-router'; import { useAxios } from '../../api/hooks/useAxios'; export interface IItem { title: string; info: string; } export default function Item() { const { id } = useParams<{id?: string}>(); const {responseData: item, isLoading, isError} = useAxios<IItem>({ method: 'get', url: `http://localhost:3000/items/${id}` }) const handleDelete = () => { //not sure what to do here. Need to make DELETE request } return ( <div> {isLoading && <p className='loading'>Loading...</p>} {isError && <p className='error'>Could Not Load Item</p>} {item && ( <> <h2>{item.title}</h2> <p>{item.info}</p> <button onClick={handleDelete}>Delete</button> </> )} </div> ) }
I could just make the axios request directly in the Item component and not use my useAxios hook, but then I would end up repeating code throughout the application.
Advertisement
Answer
Assuming your DELETE
route is the same as the GET
route, you’d just store the method type in a local state variable and change it:
const { id } = useParams<{id?: string}>(); const [method, setMethod] = useState('get'); const {responseData: item, isLoading, isError} = useAxios<IItem>({ method, url: `http://localhost:3000/items/${id}` }); const handleDelete = () => setMethod('delete');
However, I think you will realize that this only solves part of the problem, which is that you have tightly coupled your component’s return JSX with the response type of the GET
request (IItem
).