I am developing a web application using react. In my project there were multiple instances where I had to send a GET request
to the server and receive data
and loading
status. So I built a custom hook for the same
Here is the custom hook:
export const useGetApi = (link) => {
const [data, setData] = useState();
const [loading, setLoading] = useState(true);
const handleFetch = async () => {
const cancelToken = axios.CancelToken.source();
try {
const response = await axios.get(link, { cancelToken: cancelToken.token });
setLoading(false);
setData(response.data.data);
} catch (err) {
setLoading(true);
//handle error
}
};
useEffect(() => {
handleFetch();
}, [JSON.stringify(data)]);
return [data, loading];
};
For most cases, it works I only have to pass the link
as an argument to the hook. But in some instances, I have the requirement where I will have to first modify the link and then pass the modified link to the hook as a parameter.
I tried to achieve this using:
export const App=()=>{
const link='/helloworld'
const modifiedLink= getModifiedLink(link);
useEffect(()=>{
const [data,loading]= useGetApi(modifiedLink);
},[])
}
I get errors as:
React Hook "useGetApi" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.
Do I need to make a separate hook for the cases where the link is modified first and then passed? Because this will lead to a lot of repeatable code. Is there a way to use my existing hook and still fulfill my requirement. Please guide me.
CodePudding user response:
Like React hooks, Custom hooks cannot be called conditionally, inside another hook or in a loop. Custom or not hooks need to be called the top level of the component.
There are several mistakes in your code:
- your custom hook is only running once and if you want to refech it like using useEffect (I can see you tried to do this with another useEffect) you can pass the keys to your custom hooks useEffect keys. That way you can refech the query whenever you want.
export const useGetApi = (link) => {
const [data, setData] = useState();
const [loading, setLoading] = useState(true);
const handleFetch = async () => {
const cancelToken = axios.CancelToken.source();
try {
const response = await axios.get(link, { cancelToken: cancelToken.token });
setLoading(false);
setData(response.data.data);
} catch (err) {
setLoading(true);
//handle error
}
};
useEffect(() => {
handleFetch();
}, [link]); // this way your query runs whenever your link changes
return [data, loading];
};
- Using your custom hook inside another hook:
export const App=()=>{
const link='/helloworld'
const modifiedLink= getModifiedLink(link);
const [data,loading]= useGetApi(modifiedLink); // this is the true way
}
You can put the link in a useState
hook and send it to your useGetApi
so whenever the link changes your query will re-fetch.
I suggest you to use react-query, it basically does what you trying to do here but more stable and easy-to-use approach.