I am trying to create a reusable custom hook (useRequest) where I can fetch data with axios, display it and have a loading state. In case of an error I want it to be caught by useRequest. I'm having trouble catching eventual errors and passing the axios request to useRequest. Currently I'm only getting null for the error message.
EDIT: I use generated api which uses axios. So to make my fetch request it would look something like this:
import {GeneratedApi} from '/generatedApi'
const generatedApi = new GeneratedApi(configuration) //configuration is for editing the headers etc.
const response = await generatedApi.getData();
setData(response.data);
My code:
import axios, { AxiosResponse } from "axios";
import { useEffect, useState } from "react";
const useRequest = (promise: Promise<AxiosResponse<any>>) => {
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
setError(null);
await promise;
setLoading(false);
setError(null);
} catch (error) {
setLoading(false);
setError("Error: " + JSON.stringify(error));
}
};
fetchData();
}, [promise]);
return [loading, error];
};
export default function App() {
const [data, setData] = useState<any | null>(null);
const [loading, error] = useRequest(async () => {
const response = await axios.get("https://jsonplaceholder.typicode.com/todos");
setData(response.data);
return response;
});
if (loading) {
return <p>Loading ...</p>;
} else if (data) {
return <p>{data}</p>;
} else {
return <p>Error: {error}</p>;
}
}
You can pass a function, wrapped in useCallback hook, which would invoke your api call:
import axios, { AxiosResponse } from "axios";
import { useCallback, useEffect, useState } from "react";
const url = "https://jsonplaceholder.typicode.com/todos"
const useRequest = (apiCall: () => Promise<AxiosResponse<any>>, setData: (data: any) => void) => {
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
setError(null);
const response = await apiCall()
setData(response.data)
setLoading(false);
setError(null);
} catch (error) {
setLoading(false);
setData(null)
setError("Error: " + JSON.stringify(error));
}
};
fetchData();
}, [apiCall, setData]);
return [loading, error];
};
export default function App() {
const [data, setData] = useState<any | null>(null);
const fun = useCallback(() => axios.get(url), [])
const [loading, error] = useRequest(fun, setData);
if (loading) {
return <p>Loading ...</p>;
} else if (data) {
return <p>{'data'}</p>;
} else {
return <p>Error: {error}</p>;
}
}
Konstantin Samarin's answer helped to point me in the right direction.
My current solution has a missing dependency(callback) and might not be ideal. Adding the dependency causes infinite rerenders.
import axios, { AxiosResponse } from "axios";
import { useCallback, useEffect, useState } from "react";
interface RequestReponse {
loading: boolean,
error: string | null
}
function useRequest(callback: any, dependencies: any[]): [boolean, (string | null)] {
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const navigate = useNavigate();
useEffect(() => {
async function doRequest() {
try {
setError(null);
await callback();
setLoading(false);
} catch (error) {
setLoading(false);
setError(error)
}
}
doRequest();
}, dependencies); // eslint-disable-line react-hooks/exhaustive-deps
return [loading, error];
}
export default function App() {
const generatedApi = new GeneratedApi();
const [data, setData] = useState<any | null>(null);
const {loading, error} = useRequest(async () => {
const response = await generatedApi.getData();
setData(() => (response.data);
}, [])
if (loading) {
return <p>Loading ...</p>;
} else if (data) {
return <p>{'data'}</p>;
} else {
return <p>Error: {error}</p>;
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.