What is the correct way to use a hook while handling an event in React - reactjs

My goal is to fetch data from an API using a custom hook after changing a Select option.
I have a onChange event on a Select-field that looks like this:
<Select options={customers.customers} onChange={updateMarkers} />
The function updateMarkers that is being called looks like this:
const updateMarkers = (selectedOption) => {
const warehouses = GQLQuery(`
{
warehouses(where: {customer: {id: "${selectedOption.value}"}}) {
name
address {
city
lat
lng
road
zipcode
housenumber
province {
name
}
country {
name
}
}
}
}
`)
console.log(warehouses)
}
Within this function I call a custom hook (GQLQuery) that fetches the data (which works if I am not calling it from an event) that looks like this:
import { gql, useQuery } from "#apollo/client";
function GQLQuery(query) {
const { loading, error, data } = useQuery(gql`${query}`);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
if (data) return data
}
The error I get after selecting an option is:
Uncaught 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
It is obvious that I am breaking the Rules of Hooks, because I just started using React and I am missing a lot of fundamentals, but I just can't understand what I am doing wrong here. Can someone point me to the documentation that I need to understand or point me towards the solution. Thanks!

Using react hooks useQuery() inside a function breaks the rules of hooks.
You need to use query(options): Promise method of apollo client to call API manually inside your event handler.
The Apollo client should be connected to React with the ApolloProvider component. See connect-your-client-to-react.
Then, you can use it like props.client.query({...}) in the event handler of the component.

Related

RTK-query: how to have the fixedCacheKey approach also for useLazyQuery?

reading the documentation I saw that mutations can share their result using the fixedCacheKey option:
RTK Query provides an option to share results across mutation hook instances using the fixedCacheKey option. Any useMutation hooks with the same fixedCacheKey string will share results between each other when any of the trigger functions are called. This should be a unique string shared between each mutation hook instance you wish to share results.
I have a GET api call that I need to invoke using the trigger method of the useLazyQuery hook, but I need to exploit its booleans (isSuccess, isError, etc...) in many places. Is it possible to have the same behaviour for the useLazyQuery hooks ?
In RTK-query, queries instances follow this logic in cache (from documentation):
When you perform a query, RTK Query automatically serializes the request parameters and creates an internal queryCacheKey for the request. Any future request that produces the same queryCacheKey will be de-duped against the original, and will share updates if a refetch is trigged on the query from any subscribed component.
Given that the only way, in different components, to get the same cache entry is to pass the same query parameters. If you have many useLazy in many components, those hooks don't point any cache entry until you fire them (with some parameters).
This is what I can understand from the official docs
I ended up implementing a custom hook to handle this scenario
// This is the custom hook that wrap the useLazy logic
const useCustomHook = () => {
const [trigger] = useLazyGetItemQuery();
const result = apiSlice.endpoints.getItem.useQueryState(/* argument if any */);
const handleOnClick = async () => {
// The second argument for the trigger is the preferCacheValue boolean
const { data, error } = await trigger(/* argument if any */, true);
// Here where I will implement the success/error logic ...
}
return { onClick: handleOnClick, result}
}
Setting the preferCacheValue at true forced the RTQK to return the cached value if present for the lazy I'm using. That allow me both to avoid triggering many API calls.
Once implemented the hook above I can do the following:
// Component that will make the API call
function Header() {
const { onClick } = useCustomHook();
return (<Button onClick={onClick}>Click me</Button>);
}
// Component that will use the booleans
function Body() {
const { result: { isFetching } } = useCustomHook()
return (<Spin spinning={isFetching}>
<MyJSX />
</Spin>);
}
With this approach I was able both to centralize the trigger logic (e.g. which parameters I have to pass to my trigger function) and exploiting the booleans of that RTKQ hooks in many components.

getting error when I run useQuery inside the useEffect hook in react-query

When I run the below code I am getting: Uncaught 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:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
useEffect(() => {
if (user.email) {
const { data } = GetUserInfo(user.email);
}
}, [user.email]);
What I am trying to do is until I get the user email I will not run the getUserInfo query.
As mentioned in the other answer, you can only call hooks at the top level in the body of a functional component/custom hook.
I think you can achieve what you want using the enabled option in useQuery to enable/disable it based on a condition.
Docs example:
function Todos() {
const [filter, setFilter] = React.useState('')
const { data } = useQuery(
['todos', filter],
() => fetchTodos(filter),
{
// ⬇️ disabled as long as the filter is empty
enabled: !!filter
}
)
...
}
Reference:
https://tanstack.com/query/v4/docs/guides/disabling-queries
You can't call a hook inside another hook.
useEffect(() => {
if (user.email) {
const { data } = GetUserInfo(user.email);
}
}, [user.email]);
You have to call the hook outside useEffect hook
There are three common reasons you might be seeing it
You might have mismatching versions of React and React DOM.
You might be breaking the Rules of Hooks.
You might have more than one copy of React in the same app.
In your case you are breaking the rule of hooks that Call Hooks from React function components. GetUserInfo is also hook you are calling in a hook you need to call it outside of useEffect()
for reference documentation

Recoil useRecoilValue outside component

I want to build a JSON file based on a bunch of recoil values, so I created a function that fetches all the recoilVaules and returns a JSON out of it, and I call it from a component.
The thing is that it gives me an error for using useRecoilValue outside a component.
besides moving the function inside the component (which will make the component look bad because there are a lot of values) or passing all the recoilValue as parameters to the function (same), what more can I do?
This is the general idea -
const ReactCom = () => {
getJson();
....
}
const getJson = () => {
let jsonFile = useRecoilValue(someAtom);
return jsonFile;
}
Uncaught 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
This should work
const ReactCom = () => {
const json = useGetJson();
....
}
const useGetJson= () => {
let jsonFile = useRecoilValue(someAtom);
return jsonFile;
}

call one functional componet to anther functinal component in react js

I have created one functional component that name is TopLocation() and i am calling this function to another component const handleChange = e => {
TopLocation(e.target.value)
}
function TopLocation (cityid=null) {
const[location,setLocation]=useState();
let city={city_id:(cityid==''?localStorage.getItem("location"):cityid)};
useEffect(()=> {
const fetchData= async()=>{
const result = await Locationapi(
city
);
console.log('result',result);
if(result.status==200){
setLocation(result.data);
}
}
fetchData();
},[]);
it's showing error.
Uncaught 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:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
Don’t call Hooks inside loops, conditions, or nested functions
but you can call react hooks inside of your own custom hook
read more about hooks over here
https://reactjs.org/docs/hooks-intro.html

How to use a react hook in an arrow function?

I'm designing an application in react native for medical use case. The idea is to record some audio and to send it to a local server. Therefore, the ip adress is defined at runtime.
Previously, I used a global const as adress (code below).
const url = 'http://192.168.43.56:5000/';
const checkIsServerOnline = async (): Promise<string> =>
axios.get(url, {
timeout: 5000,
});
Now I would like to retrieve the url from a redux (persistant) store before using the function. I tried something like that, but it didn't worked:
function CheckIsServerOnline(): AxiosPromise {
const { ipAdress } = useSelector(
(state: RootState) => state.config
);
return axios.get(ipAdress, {
timeout: 5000,
});
}
The error message it returned:
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:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
Do you have any idea how I would be able to retrieve the value from the redux state ?
(Apologies for the begineer question)
React hooks can be used ONLY inside of React components.
Since you are using Redux I think you should set up your URL call in the store initialization. Here's a link to redux docs about initializing state: https://redux.js.org/recipes/structuring-reducers/initializing-state
Once you have it in the redux state you will be able to retrieve it wherever you want thanks to mapStateToProps from redux.

Resources