How to update a state by calling a query when you click a button using react, typescript and graphql? [duplicate] - reactjs

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.

Related

Minimizing API calls using GraphQL and React

I’ve written an API in GraphQL (Apollo Server), and a separate application in React (Utilizing Apollo Client). The app itself is really straight forward and doesn't require many calls to the API at all, and because of GraphQL, I can get all of the data a user needs in 2 calls and the only time I need to refetch the data is after a mutation.
I have a signin and a signup mutation, then two more mutations for creating an updating the main object a user interacts with.
I'm fairly familiar with React and GraphQL, but I feel like there has to be a way to make one 'larger' call after the signin/signup mutation that fetches all the data user a needs, rather than making a call to each level of nesting based on the loading of a particular component.
Mutations
SignUp
SignIn
CreateShirt
UpdateShirt
Queries
GetShirts
GetDesigns
Ideally, I could utilize a query similar to
query GetUser {
user {
id
email
shirts {
id
style
design {
name
swatch
}
}
}
}
So.. I could return all of this information in the SignIn / SignUp mutations but then after the UpdateShirt mutation, I don't have a (named) query that I can force to refetch. So then I leaned towards just creating a GetUser query that I could refetch but I don't know where to call it from.. it isn't specific to a component necessarily, more to a status of authentication.
What is the most efficient way to query my API for this information? Ignoring the ability to make nested queries and make the components ask for it seems silly.
I think, this is what you are lookin for?
https://www.apollographql.com/docs/react/data/mutations/#refetching-queries
After calling the mutation, this way, you can re-fetch your queries.
// Refetches two queries after mutation completes
const [addTodo, { data, loading, error }] = useMutation(ADD_TODO, {
refetchQueries: [
{query: GET_POST}, // DocumentNode object parsed with gql
'GetComments' // Query name
],
});
However, if you want one request then that means what you are lookin for is Batching.
You can batch multiple queries, something like:
const { data } = useQuery(gql`
query ShameelQuery {
query1
query2
}
`)
You can find more details in their official docs:
https://www.apollographql.com/blog/apollo-client/performance/batching-client-graphql-queries/

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.

Accessing cached data in RTK Query(new)

Imagine we have 3 component.
first is index which is parent.
second is filter component and third one is table component.
I used mutations for filter some data and show them in table.
In filter component I did this:
const [filterSomeData] = useFilterSomeDataMutation();
const data = filterSomeData(myFilter);
Now I need to access data in table component.
Redux toolkit query with every request cache the result , how can I access that?
Generally: If you are receiving data from the server without triggering a change on the server, you should be using a query, not a mutation. Yes, you can do POST requests with Queries and the syntax is 100% the same as with mutations.
Then you should be using that useQuery hook in all components that need that data, with the same argument as you passed in initially. That means if you have something like a filter, that you should either pass that filter in by props (by lifting the filter state up to a common parent) or keeping that filter in a Redux slice and getting it from Redux before calling your query hook.
Since you are calling that useQuery hook with the same argument in multiple components, it will not make multiple requests, but reuse the response of the first request.
What you need is api.endpoints.foo.useLazyQuery() or api.useLazyFooQuery(). This way you can assess the last fetched data on that endpoint.
Read more from the official doc: https://redux-toolkit.js.org/rtk-query/api/created-api/hooks#usequerystate
As in RTK documentation is explained, the proposed solution is calling useQuery hooks with the same arguments as before it called. It retrieves data from cached enpoints, But as I need too many arguments to send to query hook, and I should send it to other component, so I prefer to use the store object to access data from endpoints as below:
const store = useStore();
const cachedQueries = store.getState().dashboardApi.queries;
Then I made an object with endpoint's name as key and their data as value:
let dashboardResult: { [key: string]: any } = {};
Object.values(cachedQueries).forEach((item: any) => {
dashboardResult = {
...dashboardResult,
...{ [item?.endpointName]: item?.data?.data }
}
});

How to optimistically update data using useMutation

I want to update a record in a database. So far EditTodoList is updating the corresponding record in the db perfectly. My problem is with the optimistic update part the changes only show after refetching "TODO_LIST"+projectId query, even though this useMutation setup I have is supposed to be reflect the changes instantly in the ui.
I didn't face this problem when creating the todoList because in onMutate I just needed to do this queryClient.setQueryData("TODO_LIST"+projectId,(old)=>[...old,newTodoList]), but this case I need to change old array.
return useMutation(
EditTodoList,
{
onSuccess:(newProject)=>queryClient.setQueryData("TODO_LIST"+projectId,(old)=>[...old,newProject]),
onMutate:(values)=>{
const {title,projectId,todos,todoListToBeEdited}=values
queryClient.cancelQueries("TODO_LIST"+projectId)
const previousData=queryClient.getQueryData("TODO_LIST"+projectId)
//update query data
let temp=[...previousData]
const targetTodoList=temp.filter(tds=>tds.id === todoListToBeEdited.id)[0]
const targetTodoListIndex=temp.indexOf(targetTodoList)
const newTodoList = TodoListModel(projectId,title,todoListToBeEdited.orderInProject)
temp[targetTodoListIndex] = newTodoList
//this is where I need help , the setQueryData seems to ignore temp even though temp has the latest up-to-date data
queryClient.setQueryData("TODO_LIST"+projectId,(old)=>[...temp])
return queryClient.setQueryData("TODO_LIST"+projectId,previousData)
},
onError:(err,values,rollBack)=>rollBack()
}
)
the suggested way is:
optimistically update in onMutate with setQueryData. If you have a list, yes, it means iterating over that list and finding the right element to update.
return something to rollback
rollback on error
invalidate onSettled to refetch the query in any case to be in-sync with the server state. This is optional, if your optimistic update is "perfect" (like just toggling a boolean), there's sometimes no need to do that.
There's a whole section about optimistic updates in the docs, and there are also codesandbox examples
/edit: sorry, I missed the comment about the setQueryData. If you have computed the next data already, you don't need to use the functional updater. This should work:
queryClient.setQueryData("TODO_LIST"+projectId, temp)

Why is an updater/update function required to update the local cache in React Relay and Apollo Client?

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.

Resources