How to call useQuery only once with graphQL? - reactjs

So I am working on react with GraphQL and I want to call my useQuery only once ,but the problem here is that it executes everytime when page refreshes thus giving the 404 local host error.
Aim :- Want to call this query only once.
const { data, error, loading } = useQuery(getAllData);
I have also tried to achieve this by specifying the condition with useLazyQuery but still no success in that..
let check=true;
useEffect(() => {
if (check) {
check=false;
getEndpoint();
}
},[]);
const [getEndpoint, { data, error, loading }] = useLazyQuery(getAllData);
I am not able to figure any ways how to achieve that..Any Help would be appreciated !

Related

RTK Query calls not initiating before render

I have a couple of query calls that were working previously, but now are not firing off, while the many others I have throughout the app continue to work perfect. I've done some updating and adjustments elsewhere in the app, but none of which should have affected these calls. On the Redux dev tool, the calls never even show, as if they never happen. The only error I get is from the subsequent data being undefined.
UPDATE/EDIT
I've dialed in the issue, but am still confused. If I comment out the call and only display the status (isSuccess, isLoading, isError) the call goes out, completes, and returns success and I can verify the data in the devtools. However if I try to use the data, react is crashing before the data is returned.
Here's one of the calls:
import React from 'react';
import { useGetUsersQuery } from '../redux/apiSlice';
import { SupervisorTab } from './userviews/SupervisorTab';
export default function Teams() {
const { data } = useGetUsersQuery()
const teams = data.filter(e => e.role === "Supervisor")
return(
<div>
<h1>Teams</h1>
{teams && teams.map(t => <SupervisorTab supervisor={t} key={t._id} /> )}
</div>
)
}
and the corresponding endpoint on the apiSlice:
getUsers: builder.query({
query: () => '/userapi/users',
providesTags: ['User']
}),
I attempted to provide a useEffect hook to only interact with the data once the call is successful, but the same issue is occurring even within the hook.
const [teams, setTeams] = useState()
const { data, isSuccess, isLoading, isError, error } = useGetUsersQuery()
let content
if (isLoading) {
content = <h1>Loading...</h1>
} else if (isError) {
content = <h1>Error: {error}</h1>
} else if (isSuccess) {
content = <h1>Success</h1>
}
useEffect(()=>{
console.log(data)
//below are 2 scenarios that illustrate the issue, they're run at separate times...
setTeams(data)
//this will provide the correct data, set it to state, and the 2nd log below shows the same.
const teamInfo = data.filter(e => e.role === "Supervisor") //
setTeams(teamInfo)
//this call fails saying data is undefined and the initial console log states undefined
}, [isSuccess])
console.log(teams)
I've not had an issue with this before, typically I put in the query hook, it gets called and completed before the final render, without any UI crash for undefined values. Still, using useEffect, it should only interact with the data once it is available (isSuccess), yet it is crashing during the attempt to interact within useEffect.
I'm ok with React, but have not seen this behavior before. If anyone has a clue as to why or how to resolve, please let me know.

How to manage global states with React Query

i have a project that's using Nextjs and Supabase. I was using context API and now i'm trying to replace it for React Query, but i'm having a hard time doing it. First of all, can i replace context completely by React Query?
I created this hook to get the current user
export const getUser = async (): Promise<Profile> => {
const onFetch = await supabase.auth.getUser();
const userId = onFetch.data.user?.id;
let { data, error } = await supabase
.from("profiles")
.select()
.eq("id", userId)
.single();
return data;
};
export const useUser = () => {
return useQuery(["user"], () => getUser());
};
I'm not sure how to trigger it. When i was using context i was getting the user as this. If data, then it would redirect to the HomePage
let { data, error, status } = await supabase
.from("profiles")
.select()
.eq("id", id)
.single();
if (data) {
setUser(data);
return true;
}
Since i was getting the user before redirecting to any page, when i navigated to profile page, the user was already defined. How can i get the user before anything and keep this state? I suppose that once the user is already defined, at the profile component i can call useUser and just use it's data. But it's giving me undefined when i navigate to profile, i suppose that it's fetching again.
const { data, isLoading } = useUser();
But it's giving me undefined when i navigate to profile, i suppose that it's fetching again.
Once data is fetched when you call useUser, it will not be removed anymore (unless it can be garbage collected after it has been unused for some time). So if you do a client side navigation (that is not a full page reload) to another route, and you call useUser there again, you should get data back immediately, potentially with a background refetch, depending on your staleTime setting).
If you're still getting undefined, one likely error is that you are creating your QueryClient inside your app and it thus gets re-created, throwing the previous cache away. You're not showing how you do that so it's hard to say. Maybe have a look at these FAQs: https://tkdodo.eu/blog/react-query-fa-qs#2-the-queryclient-is-not-stable

RTK Query response state

I'm trying to convert some Axio code to RTK query and having some trouble. The 'data' response from RTK query doesn't seem to act like useState as I thought.
Original axio code:
const [ importantData, setImportantData ] = useState('');
useEffect(() => {
async function axiosCallToFetchData() {
const response = await axiosAPI.post('/endpoint', { payload });
const { importantData } = await response.data;
setImportantData(importantData);
}
axiosCallToFetchData()
.then((res) => res)
.catch((error) => console.log(error));
}, []);
const objectThatNeedsData.data = importantData;
New RTK Query code
const { data, isSuccess } = useGetImportantDataQuery({ payload });
if(isSuccess){
setImportantData(data.importantData);
}
const objectThatNeedsData.data = importantData;
This however is giving me an infinite render loop. Also if I try to treat the 'data' object as a state object and just throw it into my component as:
const objectThatNeedsData.data = data.importantData;
Then I get an undefined error because it's trying to load the importantData before it's completed. I feel like this should be a simple fix but I'm getting stuck. I've gone through the docs but most examples just use the if statement block to check the status. The API calls are being made atleast with RTK and getting proper responses. Any advice?
Your first problem is that you always call setImportantData during render, without checking if it is necessary - and that will always cause a rerender. If you want to do that you need to check if it is even necessary:
if(isSuccess && importantData != data.importantData){
setImportantData(data.importantData);
}
But as you noticed, that is actually not necessary - there is hardly ever any need to copy stuff into local state when you already have access to it in your component.
But if accessing data.importantData, you need to check if data is there in the first place - you forgot to check for isSuccess here.
if (isSuccess) {
objectThatNeedsData.data = data.importantData;
}
All that said, if objectThatNeedsData is not a new local variable that you are declaring during this render, you probably should not just modify that during the render in general.

Why is useEffect not working here? Do I need to change the condition?

I am trying to run this code, but useEffect is not running a single time.
export default function DetailPage() {
const [post, setPost] = useState({});
const postId = useParams().postId;
console.log(postId);
useEffect(() => {
console.log("useEffect called");
const fetchPost = async () => {
const res = await axios.get(`/posts/${postId}`);
setPost(res.data);
console.log(post);
console.log("useEffect runs");
};
fetchPost();
console.log("useEffect runs 2 ");
}, [postId]);
}
Here, I am getting postId in the console, but not "useEffect run".
I have used similar code (except I am using another variable there instead of postId) in another file, and it's working there.
Please help me with this code.
Be careful, when you use setState the value of your state is changed asynchronously. So to you, it seems that the state doesn't change, but in reality, it will change.
This because when you try to print the value of a state just after the setState, its value still wasn't updated.
When you want to debug how a state changes with console log, create a separate hook that log every change.
Add something like this:
useEffect(()=>{
console.log("Post state changed!")
console.log(post);
},[post])
For what concerns the issue that your last log (console.log("useEffect runs");) is not running the only possible issues are:
postId changes are not triggering useEffect (possible reason: you are using a ref inside your useParams to store values). To check it just put a console.log before running the Axios request:
const fetchPost = async () => {
//Does this ever display your postId? If it does go to point 2.
console.log(`Trying to fetch post ${postId}`);
//Furthermore if postId can be undefined I'll wrap this request with an if
const res = await axios.get(`/posts/${postId}`);
setPost(res.data);
console.log("useEffect runs");
};
There is an uncaught error in your network request.
First tip: always put a try-catch block when awaiting an Axios response.
This will avoid missing uncaught errors.
try {
const res = await axios.get(`/posts/${postId}`);
setPost(res.data);
} catch(e) {
//Here log your error. Remember to avoid showing the log in production
}
When working with Axios I also always suggest checking the network tab of your browser to understand what is going on with your request.
I link you a post where I explain how to do it: https://stackoverflow.com/a/66232992/14106548

React multiple http calls when navigating the application using the URL

I have a component which has filtering, searching and pagination capabilities. What I'm trying is to create a queryString and attach to the URL such that I can later copy and paste it in another browser so that I can reuse the filters.
To extract the query params from the URL I'm using the useLocation hook
const useQuery = () => new URLSearchParams(useLocation().search);
const pageNo = useQuery().get('page') ?? 1;
I'm using the useEffect hook to track for changes of the page query parameter value, and dispatch an action which will update the pageNo in the state object of my reducer.
React.useEffect(() => {
dispatch({
type: actionDescriptor.CHANGE_PAGE,
payload: pageNo
});
}, [pageNo]);
I have another useEffect hook which handles the fetch of the data, and gets triggered when the pageNo changes. I'm using the useNavigate to create and navigate to the new location if the http call was successful
const nav = useNavigate();
React.useEffect(() => {
(async function get() {
const response = // make http call and get response
if (response.status === 200) {
dispatch({
type: actionDescriptor.FETCH_SUCCESS,
payload: {
data: response.data['value'],
}
});
nav (`/data?page=${state.pageNo}`);
}
/// handle error
}
})();
}, [state.pageNo, state.pageSize, state.filter]);
When I'm navigating using the UI, selecting a different page for example, all works well, there is a single http call, the browser url is updated as expected (e.g. localhost/mydata?page=2). If however I'm copying the url and paste it in another window, it makes two http calls, and renders the dom twice. What might be the cause for this?
my guess is due to the parameters you are listening on state.pageNo, state.pageSize, state.filter. I'm assuming all of these are null/empty at the beginning of your app. Once you copied and pasted the url, two of these parameters will change which will cause the useEffect to be called twice.
put in a console.log in the useEffect to confirm that. Once that's confirmed, I would re-examine the list of parameters to see if you need to listen to all of them.
I would take a look at the pageNo. It looks like it might be changing from default value to 2 since you have 2 useEffects probably firing for the same render.

Resources