use Custom hook in a non functional api file - reactjs

I have a api file that I would like to store all my api calls and use react query to call those api function, however I have some endpoint which are private and I use a custom hooks for that logic. Issue is I cannot call the custom hook in the api file as it is not a functional component? How do I handle this
//api file
import useAxiosPrivate from "../hooks/useAxiosPrivate";
export const GetPackagesinfo = async () => {
const axiosPrivate = useAxiosPrivate();
const res = await axiosPrivate.get(
"/mk_get_packages_information"
);
return res.data;
};
export const GetAccountInfo = async () => {
const axiosPrivate = useAxiosPrivate();
const res = await axiosPrivate.get(
"/mk_company_admin_user_account_information"
);
return res.data;
};
export const DeleteAcccount = async () => {
const axiosPrivate = useAxiosPrivate();
const res = await axiosPrivate.delete(
"/mk_company_admin_user_delete_account_information"
);
return res.data;
};
if i try to use as above i get the error
Invalid hook call. Hooks can only be called inside of the body of a function component. This
could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

In short, you can't (as the warning mentions).
I don't know what is in useAxiosPrivate, but you can extract the non-React parts of that into its own non-hook function. Then both api.js and useAxiosPrivate.js can import the non-hook function.

You can't use hooks in react-query 's queryFn.
While react-query will apply your function inside like:
useSyncExternalStore(
useCallback(
()=>{
useAxiosPrivate()
},[]
),
()=>{}
)
It break the rules of React hooks.
See https://reactjs.org/warnings/invalid-hook-call-warning.html#breaking-the-rules-of-hooks

I did not find any alternative so I just copypasted each api function to its target component/page and used it from there and i was able to use my hooks as intended

Related

How to use custom hooks if data being passed to the hook needs to be modified beforehand?

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.
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.

Why "React hook is called in a function" while exporting simple single function that accesses a useAtom state?

I have this simple function that needs to set a state from jotai. I would like this function to have it's own seperate file or be cleaned up somewhere, since it will be reused. I'm new to React and come from Angular. It's kind of a function in a service Angular wise.
How would you solve this properly in React?
Code:
export const setMetamaskWallet = (): void => {
const [, setWeb3] = useAtom(web3Atom);
const [, setLoading] = useAtom(loadingAtom);
const [wallet, setWallet] = useAtom(walletAtom);
setWeb3(
new Web3(window.ethereum),
)
//Todo: Create check if metamask is in browser, otherwise throw error
const setAccount = async () => {
setLoading(true);
const accounts = await window.ethereum.request(
{
method: 'eth_requestAccounts'
},
);
setWallet(
accounts[0],
);
setLoading(false);
}
if (!wallet) {
setAccount();
}
}
Hooks can only be called in React functional components or other hooks. Here it appears you have called it in a typical function, hence the error. You could package this functionality in a custom hook. It may however be most appropriate to keep this function as it is, and instead of calling hooks within it, pass the relevant data into the function as parameters.

What is the best practice for useAxios hook with Redux's slice?

I've created a useAxios hook to use for Axios calls and also reduxjs/toolkit for handling React data. Is it the correct practice to use them together?
For example, in slice.js
export const getTodo = (data) => async (dispatch) => {
try {
const response = await axios.get(`${URL}/${data}`);
dispatch(getTodo(response.data));
} catch (err) {
throw new Error(err);
}
};
When I replace await axios.get(`${URL}/${data}`); to useAxios()hook, I got the error: hooks can only be called inside of the body of a function component.
What is the best practice for Redux to use the Axios hook? or it's not possible? Any good example? Thanks!
*The only place you can ever call a React hook is inside of a function component, or another hook. You can never call a hook anywhere else.

How to call useSWR after getting dynamic query params in nextjs?

I'm new to NextJS. I'm having trouble understanding how useSWR hook works.
I have a dynamic URL like this - /compare/:id
Example: /compare/ferrari-vs-bmw
I need to check whether the router has returned id and then split the car brands and make multiple API requests for each brand info.
Here is what I tried:
useEffect(()=>{
if(!router.isReady) return;
let queryParam = router.query.id
let brandsList = queryParam.toString().split("-vs-")
console.log('brandsList', brandsList);
getData(brandsList)
}, [router.isReady]);
const getData = (brandsList) => {
console.log("Geet Data>>>", brandsList);
for (const iterator of brandsList) {
const { data, error } = useSWR(`https://api.cars/${iterator}`, fetcher)
console.log("Data>>>", data);
console.log("error>>>", error);
}
};
I got the below error
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
What's the right way to make API requests after the query param is returned in NextJS?
You're not allowed to call useSWR inside of a function that's not a function component. instead, just fetch the data programmatically and then use setState (setCar1 and setCar2) to store the data.
Example:
const [car1, setCar1] = useState({})
const [car2, setCar2] = useState({})
useEffect(()=>{
if(!router.isReady) return;
let queryParam = router.query.id
let brandsList = queryParam.toString().split("-vs-")
console.log('brandsList', brandsList);
getData(brandsList)
}, [router.isReady]);
const getData = (brandsList) => {
console.log("Geet Data>>>", brandsList);
brandsList.forEach((brand, i) => {
fetcher(`https://api.cars/${iterator}`).then((data) => {
// If you've already called data.json() in your fetcher:
if (i === 0) {
setCar1(data)
} else {
setCar2(data)
}
})
console.log("Data>>>", data);
}).catch((error) => console.log("Error>>>", error))
};
});
Or something like that.

React: Stop hook from being called every re-rendering?

Somewhat new to React and hooks in React. I have a component that calls a communications hook inside of which a call to an API is made with AXIOS and then the JSON response is fed back to the component. The issue I'm having is the component is calling the hook like six times in a row, four of which of course come back with undefined data and then another two times which returns the expected JSON (the same both of those two times).
I did a quick console.log to double check if it was indeed the component calling the hook mulitple times or it was happening inside the hook, and it is the component.
How do I go about only have the hook called only once on demand and not multiple times like it is? Here's the part in question (not including the rest of the code in the widget because it doesn't pertain):
export default function TestWidget() {
//Fetch data from communicator
console.log("called");
const getJSONData = useCommunicatorAPI('https://jsonplaceholder.typicode.com/todos/1');
//Breakdown passed data
const {lastName, alertList, warningList} = getJSONData;
return (
<h1 id="welcomeTitle">Welcome {lastName}!</h1>
);
}
export const useCommunicatorAPI = (requestAPI, requestData) => {
const [{ data, loading, error }, refetch] = useAxios('https://jsonplaceholder.typicode.com/todos/1', []);
console.log("data in Communicator:", data);
return {data};
}
I would use the useEffect hook to do this on mount and whenever any dependencies of the request change (like if the url changed).
Here is what you will want to look at for useEffect
Here is what it might look like:
const [jsonData, setJsonData] = React.useState({})
const url = ...whatver the url is
React.useEffect(() => {
const doFetch = async () => {
const jsonData = await useAxios(url, []);;
setJsonData(jsonData)
}
doFetch();
}, [url])
...use jsonData from the useState
With the above example, the fetch will happen on mount and if the url changes.
Why not just use the hook directly?
export default function TestWidget() {
const [{ data, loading, error }, refetch] =
useAxios('https://jsonplaceholder.typicode.com/todos/1', []);
return (<h1 id="welcomeTitle">Welcome {lastName}!</h1>);
}
the empty array [] makes the hook fire once when called
Try creating a function with async/await where you fetch the data.
Here can you learn about it:
https://javascript.info/async-await

Resources