react query refetches with old query - reactjs

I have a list component that uses react-query to fetch the list data (in this case tickets)
<List
fetch={{
route: "tickets",
queryKey: "tickets"
}}
...
/>
The component fetches the data like so
const { isLoading, isError, data, refetch } = useQuery([queryKey], () => {
return fetchEntity({ route: `${route}${window.location.search}` })
})
fetchEntity does nothing else than using axios to fetch an return the data
I'm also providing window.location.search to the route because I have a filter component that sets the filter to the url.
The filter works, but when I refetch (by refocusing the window), there are 2 different requests in the network tab.
One with the filter params and the other one without the filter params. Therefore, the list shows the unfiltered data for a few milliseconds and then the filtered data again.
I also tried to use the query like so
const { isLoading, isError, data, refetch } = useQuery([queryKey, window.location.search], () => {
return fetchEntity({ route: `${route}${window.location.search}` })
})
This fixed the problem where the unfiltered data was shown for a few milliseconds. However, the old data is still fetched (maybe it is just faster now so not visible in the ui?).
Can anyone explain this behaviour of react query?

The problem was I was fetching with the same query key in a child component. That's why I had multiple requests.

Related

RTK Query how to update query state

I have a route where i fetch all categories
getAllCategoriesAdmin: builder.query({
query: () => 'categories/allAdmin',
providesTags: ['CategoriesAdmin']
})
Then I get categories data from data refactored part of useGetAllCategoriesAdminQuery
const { isLoading, isError, data = {} } = useGetAllCategoriesAdminQuery();
Now I want to implement search field where I want to update that data part with new data in other words when I create new route
getAllCategoriesAdminSearch: builder.query({
query: () => 'categories/allAdminSearch',
providesTags: ['CategoriesAdmin'],
})
Is there any way that I can reupdate data part from useGetAllCategoriesAdminQuery so I dont have to make another data from useGetAllCategoriesAdminSearchQuery
const { data = {} } = useGetAllCategoriesAdminSearchQuery();
I don't get your question completely but I'm assuming you have a query API and you need the search query for this API. I hope this helps you.
there are many ways to re-fetch the data:
1:
I think your API should support some query params for searches like this:
getAllCategoriesAdmin: builder.query({
query: (searchInput) => `categories/allAdmin/?search=${searchInput}`,
providesTags: ['CategoriesAdmin']
})
RTK query creates a cache key base on query URL and params so if you change the search input it will automatically create a new request and fetch the data.
2:
another way is using invalidateTags for example if you have a mutaion query you can invalidate the tags in this case CategoriesAdmin and that causes the RTK clear the cache for corresponding query and refech the data.
3:
refetch function. every useHookQuery in RTK has a re-fetch function that you can assign it to your search event for re-fetching the new data.
const { refetch, isLoading, isError, data = {} } = useGetAllCategoriesAdminQuery();

How to show loading state only if data is not received yet. Next.js ssr

I have multiple getServerSideProps in my project and I have a header which displays pages and I have to wait for a page to be opened once I click upon it since I need data to be fetched. Once they are fetched the page will be open.
One approach I used to show user a loading state is to use routeChangeStart BUT I stumbled upon one problem and so I would like not to use this case.
If I go on a page and the data is fetching I want to show user a spinner or some indicator and once the data is fetched I want to stop the indicator/spinner.
As you probably figured out, getServerSideProps runs on the server and is blocking. The fetch request needs to complete before the HTML is sent to the user (i.e., the page is changed). So if you want to show a loading indicator, you need to move that fetch request to the client.
For instance, if you probably have a page with this basic structure:
export default function Page({ data }) {
return <div>{data.name}</div>
}
export async function getServerSideProps() {
const response = await fetch('https://example.com/api')
const data = await response.json()
return {
props: { data },
}
}
const fetcher = url => fetch(url).then(res => res.json());
export default function Page() {
const { data } = useSWR('https://example.com/api', fetcher)
if (!data) return <LoadingSpinner />
return <div>{data.name}</div>
}
Or if you don't need SWR and can use a simple fetch request:
export default function Page() {
const [data, setData] = useState()
useEffect(() => {
fetch('https://example.com/api')
.then(async(response) => {
const json = await response.json()
setData(json)
})
})
if (!data) return <LoadingSpinner />
return <div>{data.name}</div>
}
P.S. If the initial fetch request in getServerSideProps used sensitive information (e.g., API secret credentials), then go ahead and setup a Next.js API route to handle the sensitive part and then fetch the new route.
I just used routeChangeStart.
I didn't want to use it since router.push('/map') didn't work in pages/index.tsx file but I solved this issue by creating a new component putting router.push in useeffect and rendering a loader.
routeChangeStart was in _app.js and because of this in index.js router.push() didn't work - I tested it
routeChangeStart - how it works?
When we click on a page the data is being fetched on the server and the page will only be displayed to us once the data is fetched. So we can make the next thing, we can just intercept the route change.
When we click on a link(we wait for data to fetch) we set loading state in routeChangeStart to true and if we moved to another page(it means we fetched the data) we invoke routeChangeComplete which runs once we moved to the route we wanted to, and here we set loading state to false. And after this I just pass the loading state using React Context

How to fetch data with React Query?

I'm building a Pokedex (Pokemon Index) application, using React Hook Form and React Query in which a user can submit a pokemon's name and that pokemon's data will render. I'm having trouble retrieving the individual pokemon's data. When I submit a pokemon's name, I receive a status 200 for the query, but the console prints an empty object. Please let me know what I'm doing wrong.
https://codesandbox.io/s/pokedex-5j1jf?file=/src/App.js
useQuery hook expects a function that returns a promise. So handlePokemonFetch should look like this:
const handlePokemonFetch = () => {
return axios(`https://pokeapi.co/api/v2/pokemon/${query}`);
};
Normally react-query would run the query when the component is mounted. If you do not want this behaviour, you have to disable the query by setting, enabled: false, which you actually did. If you want to trigger a fetch by yourself react-query gives you refetch function:
//notice that we also destructure the refetch function
const { isLoading, isError, data, error, refetch } = useQuery(
"pokemon",
handlePokemonFetch,
{
refetchOnWindowFocus: false,
enabled: false
}
);
//when the data is available save it on the state
if(data) setPokemonCharacter(data);
And the you call that function when the form is submitted:
<form onSubmit={()=>refetch()}>

React Redux FireStore - Changes in FireStore collection does not reflect on my screen unless I refresh

I use React with Redux and Firebase. Here is one of the functions from my Action.js
export const loadItemsInCategory = (categoryId) => {
return (dispatch) => {
let itemsArray = [];
firestoreService.getItemsInCategory(categoryId)
.then(updatedGroceryList => {
itemsArray = updatedGroceryList;
console.log(`category id is ${categoryId}`)
dispatch(loadItemsInCategoryHelper(categoryId, itemsArray))
})
.catch((error) => console.log(error));
}
}
It's a normal FireStore query. Here is what happens in firestoreService.getItemsInCategory(categoryId)
export const getItemsInCategory = async (categoryId) => {
console.log(`firebase category id is ${categoryId}`)
const snapshot = await db.collection('Item').where('category', '==', categoryId).get()
return snapshot.docs.map(doc => {console.log("called");return {id: doc.id, ...doc.data()}});
}
Right now, my application shows the list of items in the given Category. However, the list does not get updated when a new Item is added to the category by someone else. In other words, additions in FireStore collection does not reflect on my screen unless I refresh the page.
How can I code my webapp in such a way that any change on the FireStore end gets reflected on my webapp?
Thanks in advance!
Your code is doing a one-time query with get(). Queries made like this are not realtime. They don't refresh.
If you want to receive updates to your query in realtime, you should follow the documentation for realtime queries. Instead of using get(), you will use onSnapshot(). And instead of getting a promise, you will attach a listener callback that will be invoked whenever there is a change in the results of the query. Because of these differences, your code will look drastically different.

React GraphQL query results as an object without JSX?

I am coming here as a last resort! I have been searching for 1-2 weeks now and haven’t came across anything!
I want to use GraphQL in react (I have an Apollo react client and Apollo backend), but I want to be able to call a query and return the results as an object without it needing to be done using a component or class.
I have a tried using client.query but whatever way I try I only get promises returned.
I want something like this:
const myData = some kind of query call
edit:
Want I want to do is pass an array of objects into a component which will then render the data how I want it.
An example would be passing a list of events into a calendar cell to be displayed for that day.
Here's a better example of what I want to do:
const CustomCellOne = () => (
<Query query={my_query} variables={ vars }>
{({ loading, error, data }) => {
const dataPoints = data.blahQuery;
return (
<CustomCellTwo data={dataPoints} />
)
}}
</Query>
);
Network requests are asynchronous to prevent blocking UI. The Fetch API, which Apollo uses under the hood, utilizes Promises instead of the more traditional callbacks to handle asynchronously requesting resources across the network, so Apollo's API is based on Promises as well. There is no method available that will allow you to synchronously fetch a GraphQL query -- you just need to structure your code appropriately to handle the Promise once it resolves.
The following examples assume you've correctly configured a client instance. Using then:
function doSomething () {
client.query({ query: SOME_QUERY })
.then(({ data }) => {
// Use the returned data however you like
})
.catch(error => {
// Handle error
})
}
Using async/await:
async function doSomething () {
try {
const { data } = await client.query({ query: SOME_QUERY })
} catch (error) {
// Handle error
}
}
If you are unfamiliar with Promises, it would be to your benefit to review the appropriate documentation and check out some tutorials.

Resources