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.
Related
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 }
)
I'm looking for a solution to share data across a React-Query mutation without having to create my own internal state or context.
I created a custom hook that takes care of the API call.
myData.ts
const useDataMutation = () => useMutation('MY_DATA_MUTATION', postData);
Then, I use my custom hook in different components.
Component1 is in charge of mutating. The response data will be available in data once the mutate is successful.
Component1.tsx
const { mutate, data } = useDataMutation();
useEffect(() => mutate('some_data'), []);
In another nested component, I want to access the data which came back from the response. But I don't want to pass down the data to 3-4 layers of components. And I wanted to avoid using a Context to access this data.
What I want is something like this:
Component2.tsx
const { data } = useDataMutation();
console.log({ data }); // log data once available.
But in this example, the data from Component2.ts is always undefined.
Is there a simple way to achieve something like this?
Thank you.
at the moment, mutations don't share data across instances like queries do. There is an open issue about it, and contributions are welcome.
If you're using #apollo/client then you can read the previously fetched data directly from the cache.
import Query from './query.ts'
const { todo } = client.readQuery({
query: Query,
variables: {}
})
That will not fetch the data from your server again and instead fetch it from the apollo cache. So you can fetch it using the hook or in the parent component and then 5 levels down you can just pull it again from the cache using the same hook.
When you're using a mutation it will update data in the cache if the properties in the query is the same as before, so if you have a query for getUser and a mutation for updateUser I think the mutation should automatically update the getuser cache data if the data aligns with the mutation data. I'm not sure about this.
I want to do some side effects like setState and update context after the data is fetched. However, the onSuccess will not be executed when the data is in cache. Also useEffect doesn't work because if the data is cached, it doesn't change from undefined to the real data. Therefore it doesn't get trigger either. What's the best way of doing this? Thanks
My usecase is to extract some values from the data returned from useQuery and set a new state on those.
usually, they’re shouldn’t be a need to be a need to copy state from react-query into local state. This will just lead to duplication of the source of truth. It is best to keep them separated, so that you can also profit from background updates.
If you want to transform the data or subscribe to parts of the data, use the select option of useQuery:
const { data } = useQuery(key, fn, { select: data => data.map(...) })
Alternatively, you can compute some new data depending on the returned data with useMemo, e.g.:
const { data } = useQuery(...)
const articles = useMemo(() => data?.map(...), [data])
// work with articles from here on
You can also put that nicely in a custom hook.
I have a mutation (UploadTransaction) returning certain list of certain object named Transaction.
#import "TransactionFields.gql"
mutation UploadTransaction($files: [Upload!]!) {
uploadFile(files: $files){
transactions {
...TransactionFields
}
}
}
Transaction returned from backend (graphene) has id and typename field. Hence it should automatically update Transaction in the cache. In chrome dev tools for Apollo, I can see new transactions:
I also have a query GetTransactions fetching all Transaction objects.
#import "TransactionFields.gql"
query GetTransactions {
transactions {
...TransactionFields
}
}
However I don't see newly added Transaction being returned by the query. During initial load, Apollo client loaded 292 transactions which it shows under ROOT_QUERY. It keeps returning same 292 transactions. UploadTransaction mutation add new object of type "Transaction" in cache in dev-tools without affecting ROOT_QUERY in dev-tools or my query in code.
TransactionFields.gql is
fragment TransactionFields on Transaction {
id
timestamp
description
amount
category {
id
name
}
currency
}
Any idea what am I doing wrong? I am new to apollo client and graphql
From the docs:
If a mutation updates a single existing entity, Apollo Client can automatically update that entity's value in its cache when the mutation returns. To do so, the mutation must return the id of the modified entity, along with the values of the fields that were modified. Conveniently, mutations do this by default in Apollo Client...
If a mutation modifies multiple entities, or if it creates or deletes entities, the Apollo Client cache is not automatically updated to reflect the result of the mutation. To resolve this, your call to useMutation can include an update function.
If you have a query that returns a list of entities (for example, users) and then create or delete a user, Apollo has no way of knowing that the list should be updated to reflect your mutation. The reason for this is two fold
There's no way for Apollo to know what a mutation is actually doing. All it knows is what fields you are requesting and what arguments you are passing those fields. We might assume that a mutation that includes words like "insert" or "create" is inserting something on the backend but that's not a given.
There's no way to know that inserting, deleting or updating a user should update a particular query. Your query might be for all users with the name "Bob" -- if you create a user with the name "Susan", the query shouldn't be updated to reflect that addition. Similarly, if a mutation updates a user, the query might need to be updated to reflect the change. Whether it should or not ultimately boils down to business rules that only your server knows about.
So, in order to update the cache, you have two options:
Trigger a refetch of the relevant queries. You can do this by either passing a refetchQueries option to your useMutation hook, or by manually calling refetch on those queries. Since this requires one or more additional requests to your server, it's the slower and more expensive option but can be the right option when A) you don't want to inject a bunch of business logic into your client or B) the updates to the cache are complicated and extensive.
Provide an update function to your useMutation hook that tells Apollo how to update the cache based on the results of the mutation. This saves you from making any additional requests, but does mean you have to duplicate some business logic between your server and your client.
The example of using update from the docs:
update (cache, { data: { addTodo } }) {
const { todos } = cache.readQuery({ query: GET_TODOS });
cache.writeQuery({
query: GET_TODOS,
data: { todos: todos.concat([addTodo]) },
});
}
Read the docs for additional details.
I am considering using React Relay or the Apollo Client. I like the idea of GraphQL as a query language that can be executed against any API or data store.
However, I am surprised by the need to manually (and imperatively) update the store/cache after a simple mutation such as adding a todo item to a list. Here is the documentation for the updater/update functions:
http://facebook.github.io/relay/docs/en/mutations.html
https://www.apollographql.com/docs/react/essentials/mutations.html
Why is a user-defined updater function required?
Specifically, why must I write an updater function to handle running these two GraphQL queries in sequence?
//Create todo item
mutation createTodo($input: TodoInput!) {
createTodo(input: $input) {
id
name
desc
}
}
mutationVariables = { input: { name: 'Shopping', desc: 'Get groceries' }};
//Select all todos
query {
todos {
id
name
desc
}
}
Why would the todos query not return the new todo item automatically? That is the POINT of a declarative query language IMO.
The idea behind updating the local cache is to minimize the amount of data being passed between the client and the server.
Updating the local cache manually is entirely up to you. You can absolutely program your mutation to return the new or all todo's then it will update your local cache. (it will be slower than manually updating since you will need to wait for the response.) There are some good use cases for manually updating vs waiting for the response from the server
With apollo you can also turn off local cache and just use "network-only" as your fetch policy.