How to use RTK query but always fetch - reactjs

I've read about RTK query, I'm interested as it removes the hassle of writing slices & thunk action creators. However, I don't think I will want to use the cache invalidation feature. For example, in this sandbox: https://codesandbox.io/s/github/reduxjs/redux-essentials-example-app/tree/checkpoint-5-createApi/?from-embed when switching between tabs, e.g. from Notifications to Posts, I would always want to fetch Posts, but in this example, it follows the cache timer. Should I still use RTK query if I don't use the cache invalidation feature? If yes, what are clean ways to make sure when I call a component with a call to query hook, it will always fetch? Should I set the cache timer to 0s? Thanks

You can use refetchOnMountOrArgChange​ either globally or as a query hook option. Using true will always fetch, using a number allows you to set a maximum age after which a refetch occurs.
const { data } = useGetPostsQuery(
{ count: 5 },
// this overrules the api definition setting,
// forcing the query to always fetch when this component is mounted
{ refetchOnMountOrArgChange: true }
)

Related

how to clean multiple queries from cache in graphql/apollo

I am working on a web based on graphql/apollo/react.
So I want to useMutation to make post request, and when completed, I clean up some caches that belows to some queries. I read the doc of apollo, and it guide use to readquery from cache and writequery.
But I have three questions:
Can I read multiple queries cache with one client.readquery call? If the answer is no, does readquery execute synchronously?
How to clean the query cache (not update) after read them?
will the cache update or refetch(refetch in useMutation) of a query force a re-render of all the components that call the query?
my sample code:
import type {DataProxy, FetchResult} from 'react-apollo'
import {useMutation} from '#apollo/react-hooks'
const[theMutationQuery] = useMutation(MUTATIONQUERY, {
refetchQueries: [somequery expression] // will the force a rerender of all components that use 'somequery'
onCompleted: () => {},
onError: () => {},
update: (store: DataProxy, data: FetchResult<?*>) => {
const cacheDataForOtherQuery = store.readQuery({
query: GETQUERY1
variables: {
v: "str",
},
// so can i add one more query here?
})
// then how should I clean up the result from readquery, that is, cacheDataForQuery. Also, after cleaning up, will this force a rerender of all components using the query(GETQUERY1 here)
}
});
Can I read multiple queries cache with one client.readquery call?
No, you cannot read multiple queries' cache with one client.readQuery() call. You need to call client.readQuery() multiple times, once for each query you want to read from the cache.
If the answer is no, does readquery execute synchronously?
Yes, client.readQuery() executes synchronously. It immediately reads the query data from the cache and returns it.
How to clean the query cache (not update) after read them?
To clean the cache for a specific query, you can use the client.evict() method. This method takes in a options object with a query property that should be the GraphQL document representing the query you want to evict. This method will remove the query and it's data from the cache.
client.evict({
query: GETQUERY1,
variables: { v: "str" }
});
will the cache update or refetch(refetch in useMutation) of a query force a re-render of all the components that call the query?
Yes, when you update or refetch a query, all the components that are using that query will be re-rendered to reflect the updated data. This is because the Apollo client will notify those components that the data they are using has changed, and they will update their state accordingly.
Regarding your sample code, by using the update function you can manipulate the store's cache, you can use it to update a specific query data, but also you can use it as you did to read a query data and then use the data to update or clean other queries. The refetchQueries option in the mutation configuration is used to refetch a set of queries after a mutation is completed. Keep in mind that refetching a query will cause all components that are using that query to re-render and reflect the new data.

How can I invalidateQueries, react-query, within a redux-toolkit reducer?

As its probably obvious, I am trying to have a couple of APIs, used within the redux-toolkit reducer all invalidate my queries, react-query. BUT, I can't get it to work because of not being able to use "hooks" within the reducer. Instead of tracking down all the api calls.. and then invalidating "n" number of times, I figure to just do something like... but of course, not working. Anyone have ingenius way of invaliding queries from "n" number of api calls?
.addMatcher(
(action): action is AnyAction =>
[
asyncActions.someAction1.fulfilled,
asyncActions.someAction2.fulfilled,
asyncActions.someAction3.fulfilled,
asyncActions.someAction4.fulfilled,
].some(actionCreator => actionCreator.match(action)),
(state, action: AnyAction) => {
// INVALIDATE MY QUERIES HERE
}
)
NOTE: I know redux-toolkit has it's own query. But I have a fast project and don't have time to learn it and also, by upgrading my toolkit, it creates massive amounts of TS errors (I guess a lot TS changes with later versions)...
You are not allowed to trigger any side effect in a Redux Reducer, ever. You will need to find another place to do something like this - if you want to tie it to the Redux action being dispatched, you could look into the listener middleware.

ReactQuery - useInfiniteQuery refetching issue

I have implemented infinite scroll on a project that is using React Query library.
So far so good. Everything works as expected using the useInfiniteScroll hook
One issue that I am encountering is the caching mechanism of this library.
If I query for a resource, ex: GET /posts/, will trigger a GET /posts/?page=0, scroll a bit down, get the next results, GET /posts/?page=1 which is totally fine. If I add search parameter to the page will do a GET /posts/?filter=someValue&page=0. All fine... but when I remove the filter from the search it will automatically do GET /posts/?page=0 & GET /posts/?page=1
A solution is to remove the query from the cache using the remove() method from the query itself. But this means that I have to manually do it for each query.
I would like to get a better solution that will cover all cases. I have a queryWrapper where I want to handle this.
I tried using the queryClient instances invalidateQueries and resetQueries but none seems to be able to remove it from the cache...
In the examples below I keep a ref for the params, if they are changed I can trigger the query to reset via useLayoutEffect hook. This part works as expected
invalidateQueries attempt
queryClient.invalidateQueries(
[queryKey, JSON.stringify(paramsRef.current)],
{
exact: true,
refetchActive: false,
refetchInactive: false
},
{ cancelRefetch: true }
);
resetQueries attempt
queryClient
.resetQueries([queryKey, JSON.stringify(paramsRef.current)], {
refetchPage: (page, index) => index === 0
})
I even tried the queryClient.clear() method which should remove all existing queries from the cache but still the page number somehow remains cached... I access the queryClient using useQueryClient hook. If I inspect it, I can see the queries inside.
Can someone please help me to sort this cache issue
Thanks!
but when I remove the filter from the search it will automatically do GET /posts/?page=0 & GET /posts/?page=1
This is the default react-query refetching behaviour: An infinite query only has one cache entry, with multiple pages. When a refetch happens, all currently visible pages will be refetched. This is documented here.
This is necessary for consistency, because if for example one entry was removed from page 1, and we wouldn't refetch page 2, the first entry on page 2 (old) would be equal to the last entry of page 1 (new).
Now the real question is, do you want a refetch or not when the filter changes? If not, then setting a staleTime would be the best solution. If you do want a refetch, refetching all pages is the safest option. Otherwise, you can try to remove all pages but the first one with queryClient.setQueryData when your query unmounts. react-query won't do that automatically because why would we delete data that the user has scrolled down to to see already.
Also note that for imperative refetches, like invalidateQueries, you have the option to pass a refetchPage function, where you can return a boolean for each page to indicate if it should be refetched or not. This is helpful if you only update one item and only want to refetch the page where this item resides. This is documented here.

apollo-client used to make multiple requests to make a real-time search

We're developing a search engine inside an app with Apollo and we do not know exactly how to develop a real-time search engine that makes a request to the server on every keyboard press.
On the documentation it says that we must use the new <Query /> component, but I see that this case mostly fits with firing a manual query: https://www.apollographql.com/docs/react/essentials/queries.html#manual-query
I don't know if I'm correct, or maybe we should use it in another way.
thanks!
As it is said at a link you shared, if you wanted to delay firing your query until the user performs an action (your case), such as clicking on a button, you want to use an ApolloConsumer component and directly call client.query() instead.
Query component can't be used in this situation because when React mounts a Query component, Apollo Client automatically fires off your query during rendering.
UPDATE
With Apollo Client V2.6, it is now possible to make a Query to the server manually using a hook. The hook that you want is useLazyQuery.
You'd have something like this;
const [onSearch, { called, loading, data }] = useLazyQuery(
SEARCH_QUERY,
{ variables: { searchText: state.searchText } }
);
Then you can call the onSearch function whenever your text changes inside a useEffect like below.
useEffect(() => {
onSearch()
}, [state.searchText])
Note that you might want to dounce your onSearch function such that you don't hammer your server on every key stroke.

ReactJS fetching new data on prop

As a preface, I'm still new to React, so I'm still fumbling my way through things.
What I have is a component that fetches data to render an HTML table. So I call my Actions' fetchData() (which uses the browser's fetch() API) from within componentWillMount(), which also has a listener for a Store change. This all works well and good, and I'm able to retrieve and render data.
Now the next step. I want to be able to fetch new data when the component's props is updated. But I'm not exactly sure what the proper way to do so is. So I have a three part question
Would the proper place to do my fetchData() on new props be in componentWillReceiveProps(), after validating that the props did change, of course?
My API is rather slow, so it's entirely possible a new prop comes in while a fetch is still running. Is it possible to cancel the old fetch and start a new one, or at least implement logic to ignore the original result and wait for the results from the newer fetch?
Related to the above question, is there a way to ensure only one fetch is running at any time besides having something like an isLoading boolean in my Action's state (or elsewhere)?
Yes, componentWillReceiveProps is the proper place to do that.
Regarding point 2 and 3:
The idea of cancelling the task and maintaining 'one fetch running' seems to be inadequate. I don't think this kind of solution should be used in any system because implementation would limit an efficiency of your app by design.
Is it possible to cancel the old fetch and start a new one, or at least implement logic to ignore the original result and wait for the results from the newer fetch?
Why don't you let a 'newer fetch' response override an 'old fetch' response?
If you really want to avoid displaying the old response you can implement it simply using a counter of all fetchData calls. You can implement it in this way:
var ApiClient = {
processing: 0,
fetchData: function(){
processing++
return yourLibForHTTPCall.get('http://endpoint').then(function (response)){
processing--
return response
}
},
isIdle: function(){
return processing == 0
}
}
and the place where you actually make a call:
apiClient.fetchData(function(response){
if(apiClient.isIdle()){
this.setState({
})
}
}
I hope yourLibForHTTPCall.get returns a Promise in your case.

Resources