I'm building a react app, I have a datasheet and update directly on the data stream
I want when I send data to graphql server for update and get true result then I will update apollo cache with cache.writeQuery
The problem is that when the following code is executed, there is also a request to the graphql server to get the data from the whole table and update the cache, I don't want to request to the graphql server to work. There, I want to update from the browser. So where did I go wrong?
here is my code
updateInventoryCache: async (_, { inventory, productId, variables }, { cache }) => {
let variablesData;
if (variables) {
variablesData = JSON.parse(variables);
}
const { getListProduct } = cache.readQuery({
query: GET_PAGING_PRODUCT,
variables: variablesData.variables
});
cache.writeQuery({
query: GET_PAGING_PRODUCT,
variables: variablesData.variables,
data: {
getListProduct: {
...getListProduct,
products: getListProduct.products.map((product) => {
if (product.id === productId) {
return {
...product,
inventory
};
}
return product;
})
}
}
});
return true;
}
"#apollo/client": "^3.3.7"
update 1:
I will initially call the graphql server to get the data and store it in apollo's (cache-and-network) cache. Then I want to update that data in the cache without having to call the apollo server to refetchQueries As in the post, I used the client.writeQuery function to update the cache but instead of updating at the client, apollo called the graphql server to get new data and update the cache while I was not using refetchQueries.
update 2:
I checked, my cache has been updated but my UI doesn't re-render
I believe what you're looking for is nextFetchPolicy="cache-first":
https://www.apollographql.com/docs/react/data/queries/#usequery-api
nextFetchPolicy
FetchPolicy to begin enforcing
after the current request. Useful for switching back to cache-first
after cache-and-network or network-only.
After your call to cache.writeQuery your datasheet query will then check the cache-first to see if all of it's required data is there. If you get a cache hit, it will return data immediately without loading.
Keep in mind with AC3, sometimes multiple queries can share the same cache object but request different fields. If either cache.writeQuery or your typePolicies merge causes an active query field to be missing from the cache, it will result in a cache miss for that query. I.e. An active query that once had valid data, will suddenly return undefined. More on that issue here.
Use fetchPolicy="cache-only" to use only the cache https://www.apollographql.com/docs/react/data/queries/#setting-a-fetch-policy
Related
I am caching product data coming from /products/name API using the Products tag and I want to re-fetch only when the product list has been updated.
But I am concerned that I could still get cached data if someone else (another user) has updated the products and I didn't call /add-product API.
Then what's the use case of caching data using tags in using createApi?
...
endpoints: (builder) => ({
getProducts: builder.query({
query: () => `/products`,
provideTags: 'Products',
}),
addProduct: builder.mutation({
query: ({ product }) => ({
url: `/add-product`,
method: 'put',
body: product,
}),
invalidateTags: 'Products'
}),
...
RTK Query's main purpose is to help keep data on the client in sync with what's on the server.
As part of that, the philosophy of RTK is that it's best to re-fetch data from the server any time something may have changed, so that the client always has the most current data.
"Tags" are used to manage relationships between mutations and queries. When you run a mutation, you're telling the server "go update this data". That means the data on the server is now newer than what's on the client, so we should re-fetch the related data and keep the client up to date. By attaching the same tag from a query's providesTags into a mutation in invalidatesTags, RTKQ knows that any time the mutation runs it should re-fetch the related queries.
If you think there's cases when someone else other than this user may have updated the data on the server, you can also configure RTKQ to automatically poll and re-fetch the data on a timer.
More details:
https://redux.js.org/tutorials/essentials/part-8-rtk-query-advanced#cache-data-subscription-lifetimes
https://redux-toolkit.js.org/rtk-query/usage/automated-refetching
https://redux-toolkit.js.org/rtk-query/usage/polling
This must be user error, but I've got an app with a simple currentUser query that looks at a JWT for an id, looks it up, and returns the appropriate user.
I can look at devtools and see that it's in the cache as __ref:User:19
export const CURRENT_USER_QUERY = gql`
query{
currentUser {
id
fullName
email
}
}
`
But in my component, when I do const { currentUser } = client.readQuery({ query: CURRENT_USER_QUERY }); it (intermittently) blows up because:
Cannot destructure property 'currentUser' of 'client.readQuery(...)' as it is null.
User:19 exists and is all the stuff I need. I hit reload, and that same page works.
I have my default error policy set to "all" in the client.
This is version 3.3.11 of the client. Chrome v88.04, for what that's worth. React 17.01.
Am I missing something here? Should that not return a value synchronously (and dependably) if that item's in the cache?
Is there a better way to deal with that situation? I'm trying to move this app away from storing things in redux or some context provider since it's already being handled by Apollo. Seems redundant to have that responsibility handled by a bunch of different things.
I was facing issues with .readQuery() last night. I was getting null returned everytime, though the logic was right. I was calling .readQuery() within a component I imported into my React page.
What ended up being my issue is that I was not updating the same query I made in the "parent" react page as the one in the component.
I don't know if this is the same problem you're running into, but I thought I'd leave this here for perpetuity and perspective.
I was facing the same issue. I fixed it like this:
const existingData = cache.readQuery({
query: GET_CONTRACT,
variables: {
...variables, // fixed: passing the same reference to variables
},
});
The problem here can be in new variables or absence of variables. So the query was made with variables and you try to get it from the cache without. .readQuery from cache must be identical
You need to pass the same variable with the same values you use when executing the mutation and in the same order
existingData = cache.readQuery({
query: QUERY_NAME,
variables: {
x: mutationValue1,
y: mutationValue2
z: mutationValue3
},
});`
I am working on an app in React+TS/GraphQL/Prisma. The API is written in GraphQL and when I run a particular query in the playground, I get the data I expect, but when I run the exact same query in react, I get null. Why would this be?
What I run in query (relevant portion)
query {
session {
userData: {
firstName
lastName
dateOfBirth
}
}
}
and in the playground it returns
{
"data": {
"session": {
"userData": {
"firstName":"John",
"lastName":"Doe",
"dateOfBirth":null,
}
}
}
}
but in the App, when I console log the userData or data.session it returns null for the whole object. console.log(data) prints
session: {
userData: null
}
does anyone have any ideas why this would be? The playground accurately pulls user data and populates the subfields, but on the App in the browser, it only returns "null".
I discovered the solution and wanted to share it here.
The problem came because we are working on a session with data that updates. So user creates a session, then updates, then the next step updates some more, etc before finally submitting the session.
The problem described in my question is based on Apollo defaulting to cache-first as it's fetch policy. So when the user updates, the next step queries the non-updated, cached version of the session data.
To fix this, at each step in the process, we need to call our query and set the fetch policy to "network-only" like so:
const {loading, error, data} = useSessionQuery({fetchPolicy: 'network-only'});
This will ensure that data is pulled from the network where your updateMutation was performed instead of the cached (outdated) copy.
I'me learning the local state management capabilities of Apollo Client 2.5 and finding it rather confusing. For what it's worth, I was able to understand Apollo Server pretty readily, but this is proving more daunting.
I (kind of) understand how to use it when one wants to set up local resolvers and queries to read local state, but is there a way to simply set some local state variables, as one can pretty easily with Redux. For example, setting isLoggedIn as true on the completion of a login mutation? I tried this (following https://www.apollographql.com/docs/react/essentials/local-state)
return <Mutation mutation={LOGIN} onCompleted={data => {
if (data && data.login) {
const {login: {token}} = data
if (token) {
history.push('/list')
}
}
}}>
{(register, {data, error, loading, client}) => {
if (error) {
return <div>{error.toString()}</div>
}
if (loading) return <div>Loading...</div>
if (data && data.login) {
const {login: {token}} = data
if (token) {
localStorage.setItem('token', token)
client.writeData({isLoggedIn:true})
}
but got the error
Error: Error writing result to store for query:
{"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GeneratedClientQuery"},"selectionSet":null}]}
Cannot read property 'selections' of null
But I don't know if I'm using writeData correctly. More importantly, how to I read the data? There doesn't seem to be a corresponding readData call in the API. Do I need to set up a GraphQL query and run that to see if the user is logged in? Do I need to then make a resolver for that query and include it when constructing the Apollo Client?
I saw some examples using apollo-link-state that seemed to be simpler, but apparently that's deprecated and included in Apollo Client 2.5.
Clearly I am not seeing the forest for the trees, and would welcome any advice.
You almost have it , you just forgot to pass the data in the data object instead of directly ==>
client.writeData({ data: { isLoggedIn: false } });
I have a relay mutation that posts some data to my server. My app shouldn't wait for the response before continuing.
I know I can execute arbitrary queries with the following:
const query = Relay.createQuery(Relay.QL`
query {
viewer {
searchInterests(prefix: $prefix, first: 10) {
edges {
node {
id
name
}
}
}
},
}
`, {prefix: input});
Relay.Store.primeCache({query}, readyState => {
if (readyState.done) {
// When all data is ready, read the data from the cache:
const data = Relay.Store.readQuery(query)[0];
...
}
How can I fire off mutations asynchronously without my app waiting for the response?
When designing a fat query, consider all of the data that might change as a result of the mutation – not just the data currently in use by your application. We don't need to worry about overfetching; this query is never executed without first intersecting it with a ‘tracked query’ of the data our application actually needs. If we omit fields in the fat query, we might observe data inconsistencies in the future when we add views with new data dependencies, or add new data dependencies to existing views.