I don't want my components to always refetch when they will mount and already have the query in the cache, so I did:
export const App = ({ props }) => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnMount: false,
},
},
})
return (<QueryClientProvider client={queryClient}>{...restOfMyApp}</QueryClientProvider>)
}
When I save some config I tried to invalidate my query with the first key and with that queryClient.invalidateQueries(). In the React-Query devtools, it did show that the query is invalidated, but it just keeps the same results and don't refetch the query.
How can I invalidate the query to be refetched, but don't have to launch the query everytime the component mounts?
Thank you for your help!
It sounds like there's a bug in your code; could you show how you're invalidating the cache, and your cache keys?
Using refetchInactive should definitely solve the issue for you (emphasis mine):
When set to true, queries that match the refetch predicate and are not being rendered via useQuery and friends will be both marked as invalid and also refetched in the background
Here's a quick demo on stackblitz.
const client = useQueryClient();
client.invalidateQueries(YOUR_CACHE_KEY, { refetchInactive: true });
if your query is active, it should refetch with invalidateQueries. If its not active, as others have mentioned, set refetchInactive: true.
How can I invalidate the query to be refetched, but don't have to launch the query everytime the component mounts?
Often, caching is better described with a time-based approach, so the better solution to turning off the flags would be to set a staleTime to define how long your data is going to be valid, rather than to define certain points where you want / do not want a refetch.
if you set the staleTime for your query to, say, 20 minutes, none of the refetch events (onMount, onWindowFocus, onReconnect) will trigger a refetch in that time frame. invalidating it manually will still refetch it.
This would also be in-line with how HTTP caching works (think: Cache-Control: max-age=60)
to represent: fetch once, then never again (unless garbage collected), set staleTime: Infinity.
Related
If you have a pending query when a mutation with an optimisticResponse is executed, the optimisticResponse doesn’t get applied.
const {data, refetch} = useQuery(GET_TODOS);
const [updateTodo] = useMutation(UPDATE_TODO);
// in a form submit handler:
refetch();
// Immediately mutate while the _query_ is pending
updateTodo({
variables: { id, description: description + 1},
optimisticResponse: {
updateTodo: {
id,
__typename: "Todo",
description: description + 1
}
}
});
Minimal codesandbox.io example. There’s an artificial 1 second delay link added to make the effect more obvious.
The same behaviour appears to occur with direct cache writes as well; writes will not cause a re-render if there is a pending read query.
The same behaviour can also be witnessed if batching a query in with a mutation.
Is this the intended behaviour? And if so, is there a way to bypass it?
The Apollo useQuery hook uses a default fetch-policy of cache-first. Internally when the Apollo cache is updated the following occurs
Check if any queries are observing that part of the cache
Check if they should be notified of the update
Notify
When checking whether to notify a query, there is a check to see if the query is currently in flight to the server and if so only notify when the fetch-policy is cache-only or cache-and-network.
This is fine, and makes sense, you don't want to spend CPU re-rendering when you know the data is just about to update.
This causes a problem in the example above due to the refetch query being in progress when the optimistic update is applied. The shouldNotify check will return false. Changing the queries fetch policy fixes this
const {data, refetch} = useQuery(GET_TODOS, {
fetchPolicy: 'cache-and-network'
});
for example, i've a component to fetch and call an API, but that query by default is enabled: false and i should fire that by onClick:
const query = useQuery('key', fetch, { enabled: false })
const exec = () => query.refetch()
return <button onClick={exec}>Load</button>
But i've a new API call after every clicks on the button, actually i want to cancel re-calling the API still the cached data is available and is not stale...
I there any way to implement something like refetch to retrieve cached data but without re-calling the API? our basically react-query has a re-call for any data reteive?
in fact, our data doesn't change frequently and is fix for 2-3 days...
other words, our clients frequently work with nested drop-downs with same API calls and i want to reduce same key queries... imagine that, something like category to select a brand for products
Thanks
You can set staleTime and cacheTime options of your query to Infinity to keep your cache always available
https://react-query.tanstack.com/reference/useQuery
imagine that, something like category to select a brand for products
This is a classic example for react-query where you want to put all dependencies of your query into your cache key (see the official docs). That way, the caches won't override each other, and you'll instantly get the values back from the cache if they are available. Not that you will also get a background refetch, and that's where staleTime comes in. If you don't want that, set a higher staleTime to only retrieve the value from the cache if it exists.
To illustrate, let's take your example of a select of categories for products:
function MyComponent() {
const [category, setCategory] = useState(null)
const { data } = useQuery(['key', category], () => fetchProducts(category), { enabled: !!brand, staleTime: 1000 * 60 * 3 })
<CategorySelect onSelect={setCategory} />
}
Multiple things going on here:
I have some local state, where I store the selected category
This selection drives the query. The query is disabled as long as I have no selection, but enables once the user makes a selection
every time the category changes, the query key changes, which will make react-query trigger a refetch automatically, for the new data
I've set the staleTime to 3 minutes. If I chose a category that I have already chosen, I will get data from the cache. Within 3 minutes, I will only get it from the cache. If my data is older, I will get it from the cache and the data will also be updated in the background.
I have a react query to get user data like this
const { data: queryInfo, status: queryInfoLoading } = useQuery('users', () =>
getUsers()),
);
I then have a sibling component that needs the same data from the get users query. Is there a way to get the results of the get users query without re-running the query?
Essentially, I would like to do something like this
const userResults = dataFromUserQuery
const { data: newInfo, status: newInfoLoading } = useQuery('newUserData', () =>
getNewUsers(userResults.name)),
)
As suggested in this related question (how can i access my queries from react-query?), writing a custom hook and reusing it wherever you need the data is the recommended approach.
Per default, react-query will trigger a background refetch when a new subscriber mounts to keep the data in the cache up-to-date. You can set a staleTime on the query to tell the library how long some data is considered fresh. In that time, the data will always come from the cache if it exists and no refreshes will be triggered.
We are developing APIs using Apollo GraphQL. We use the out of the box caching solution that Apollo provides (KeyValueCache using a Redis datastore).
When a request query arrives, why does ApolloServer check the cache first before it does anything else?
Is there any way to insert logic before the cache is touched? For example, we want to do some authentication and permissions checking before Apollo checks the cache.
(Yes, there are directives but we find Public/Private scope and maxAge insufficient for our needs.)
The code and explanation below flags a few different approaches for you to explore -- hopefully one will suit your needs (I am assuming you know you can control whether Apollo looks to cache first by fetchPolicy - although I discuss this briefly below). First, consider using a HOC that checks permissions and authentication prior to returning the passed Component. The permissions/auth data can be passed as props should the passed Component be rendered.
withUserData = Component => {
const { isValidated, userData } = checkAuthAndPermissions(); // Modify for your implementation
if (!isValidated) return null;
return <Component userData={userData} />
}
You can thereafter wrap any Component that needs to make the auth/permission check with the HOC, as shown below. As also shown below, Apollo provides the opportunity to skip the query altogether looking to props or other logic, if that is something you may consider. Finally, through the option prop, you have the ability to set the fetchPolicy, which could be dynamically based on a permission check or props. With this fetchPolicy you could avoid looking to cache if that is an objective.
const ComponentWithApollo = graphql(YOUR_QUERY, {
skip: props => { /* consider permissions/auth here, skip if needed */ },
options: props => {
const fetchPolicy = determineFetchPolicyFromAuthOrPermissions();
return { fetchPolicy };
},
props: ({ data }) => data
})(YourComponent);
withUserData(ComponentWithApollo);
I am using react-apollo and have been for quite some time. One thing that has already been a problem for me is the fact that refetch doesn't work when using a mutation This has been a know issue for as long as I have been using the app.
I have got round this by using the refetch prop that is available on a query.
<Query query={query} fetchPolicy={fetchPolicy} {...props}>
{({ loading, data, error, refetch }) => {
... pass down to mutation
</Query>
However I am now reading in the documentation that you recieve
an update method as part of a mutation and you should use this to update your application after a mutation.
Can you use the update function to update your UI's data and have it update after finishing a mutation? If you can, is this the standard way to do updates now?
*Using refetchQueries not working
As you can see in the image the console.info() displays that the data.status = "CREATED"; but the request coming back from the mutation directly is data.status = "PICKED"; PICKED is the correct and uptodate information in the DB.
In order of preference, your options are:
Do nothing. For regular updates to an individual node, as long as the mutation returns the mutated result, Apollo will update the cache automatically for you. When this fails to work as expected, it's usually because the query is missing the id (or _id) field. When an id field is not available, a custom dataIdFromObject function should be provided to the InMemoryCache constructor. Automatic cache updates also fail when people set the addTypename option to false.
Use update. The update function will run after your mutation completes, and lets you manipulate the cache directly. This is necessary if the mutation affects a field returning a list of nodes. Unlike simple updates, Apollo has no way to infer whether the list should be updated (and how) following the mutation, so we have to directly update the cache ourselves. This is typically necessary following create and delete mutations, but may also be needed after an update mutation if the updated node should be added or removed to some field that returns a list. The docs go into a good deal of detail explaining how to do this.
<Mutation
mutation={ADD_TODO}
update={(cache, { data: { addTodo } }) => {
const { todos } = cache.readQuery({ query: GET_TODOS });
cache.writeQuery({
query: GET_TODOS,
data: { todos: todos.concat([addTodo]) },
});
}}
>
{(addTodo) =>(...)}
</Mutation>
Use refetchQueries. Instead of updating the cache, you may also provide a refetchQueries function, which should return an array of objects representing the queries to refetch. This is generally less desirable than using update since it requires one or more additional calls to the server. However, it may be necessary if the mutation does not return enough information to correctly update the cache manually. NOTE: The returned array may also be an array of strings representing operation names, though this is not well documented.
<Mutation
mutation={ADD_TODO}
refetchQueries={() => [
{ query: TODOS_QUERY, variables: { foo: 'BAR' } },
]}
>
{(addTodo) =>(...)}
</Mutation>
Use refetch. As you already showed in your question, it's possible to use the refetch function provided by a Query component inside your Mutation component to refetch that specific query. This is fine if your Mutation component is already nested inside the Query component, but generally using refetchQueries will be a cleaner solution, particularly if multiple queries need to be refetched.
Use updateQueries. This is a legacy option that's no longer well-documented, but provided similar functionality to update before update was added. It should not be used as it may be deprecated in the future.
UPDATE:
You may also set up your schema in such a way that queries can be refetched as part of your mutation. See this article for more details.