Apollo Graphql relayStylePagination prevents query to refetch on variables change - reactjs

When I use relayStylePagination helper funtion in Apollo Client cache type policies, changing the variables passed to the query doesn't call the query again even if I call the refetch function manually.
It perfectly does the fetchMore behavior and merges the new data.
My cache field looks like this:
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
inventories: relayStylePagination(),
},
},
},
})

I needed to manually call refetch while passing the new variables.
Still have no clue why adding relay type policy prevents triggering refetch automatically.

Related

Apollo Client 3 - how to properly implement optimistic response?

I have some queries and mutations and one mutation updates quite big entity so I want to add optimistic response after mutate function is fired. The thing is that even if I pass to optimisticResponse object, full data that will be also returned when mutation completes the job it does not add it to cache - it seems that data is refreshed when mutation response is ready since having optimistic response or not the time of updating UI is the same so I assume optimistic response does not work.
Some code examples I have:
Mutation:
mutation UpdateList($id: ID, $data: ListData) {
updateList(id: $id, data: $data) {
list_id // 1
name
}
}
action
const [action] = useMutation(mutation_from_above)
// async body function so await can be used
await action({ variables: { id: 1, data: { name: 'secret name' }}, optimisticResponse: {
updateList: {
__typename: 'List',
list_id: 1,
name: 'some updated name until data is back'
}
})
and of course I have updated my id field for typename in the cache config like that:
cache: new InMemoryCache({
typePolicies: {
List: {
keyFields: ['list_id'],
},
},
}),
and it looks pretty simple but for me it does not work. I also checked on API side and response from mutation is the same I pass to the optimisticResponse object. Is there some important point why it is not working ? Can someone explain me what to do in order to get this work ?
Thanks, cheers!

Apollo client v3 not caching query results with useQuery

I am using apollo v3 with a create-react app. I fire a query with useQuery and expect results to be cached, but they are not.
In my App.jsx, I have:
const client = new ApolloClient({
uri: `${api}/graphql`,
cache: new InMemoryCache()
})
I wrap my app with the ApolloProvider.
I have a provider that uses this graphql query to fetch a list of users:
const USERS_QUERY = gql`
query GetUsers {
users {
id
email
fullName
}
}
`
The query works, when I inspect the apollo tab in chrome devtools, I see nothing in the cache section.
My questions:
Why are the results not cached if I am using the useQuery from #apollo/client?
const { loading, error, data } = useQuery(USERS_QUERY)
I thought results should be cached automatically.
I also tried to add a type policy:
cache: new InMemoryCache({
typePolicies: {
Users: {
keyFields: ['id']
}
}
})
But I think I use this if I want to normalise with a different key the cache, or if I want to decide how to merge new data by myself. But first I need to have the data in my cache
As far as I know, this is because your query does not have any input arguments / variables, so the cache does not know what item to get from the cache when the query is called again. From what I understand, the cache is only used when a specific piece of data is looked for with an ID; otherwise, if it is a generic query, the data might have changed and so is not cached.

WriteQuery not writing object to apollo cache

I am trying to use Apollo Cache specifically for local state management. I am using Apollo Client 3. I am trying to write default state ( a simple object ) using writeQuery in the cache, but it doesn't get written in the cache. I can't find what I am missing here.
const DEFAULT_STATE_QUERY = gql`
query defaultState{
id
name
age
}
`;
and
client.writeQuery({
query: DEFAULT_STATE_QUERY,
data: {
defaultState: {
__typename: "DefaultState",
id: 1,
name: "Biryani",
age: 45
},
},
});
I checked that we don't need typeDefs for reading and writing to cache as long as we don't want introspection to work out in apollo dev-tools for example.
Hence I initialised a simple client
const client = new ApolloClient({
cache: new InMemoryCache()
});
and provided it to my components using ApolloProvider
Inspite of running the code which does client.writeQuery, I dont see anything in Apollo cache and expectedly, one of the components running a useQuery too returns undefined data. What am I missing ?

RTK Query get state from another slice using getState()

I just started on redux yesterday and after reading up on the different libraries, I decided to use the slice route from RTK.
For my async, instead of using createAsyncThunk, I decided to use RTK query and I have a question on the right way to access state from another slice.
slice1 contains some user data for example:
export const initialState: IUserState = {
name: 'example',
id: null,
};
and in my slice2, I have a function that wants to do something like getSomethingByUserId(id) and my current implementation:
interface IApiResponse {
success: true;
result: IGotSomethingData[];
}
const getsomethingSlice: any = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: 'https://someapibase',
}),
endpoints(builder) {
return {
fetchAccountAssetsById: builder.query<IApiResponse, null>({
query() {
console.log('store can be called here', store.getState().user.id);
return `/apipath?id=${store.getState().user.id}`;
},
}),
};
},
});
export default getsomethingSlice;
export const { useFetchAccountAssetsByIdQuery } = getsomethingSlice;
As I read somewhere that markerikson mentioned it's not good practice to import the store but to use getState in thunk, I took a look around and see in the documentations that there is getState for query which exist in the onStart unlike thunk which you can access it from it's second parameter.
Do anyone have a onStart implementation for this? Or is importing store acceptable for this?
Generally, we want to prevent people from doing that, which is why you don't have getStore available there (you have at many other places).
You see, RTK-query uses the argument you give into the query to determine the cache key.
Since you don't pass in an argument, the cache key the result would be stored as fetchAccountAssetsById(undefined).
So, you make a first request, state.user.id is 5 and that request is made.
Now, your state.user.id changes to 6. But your component calls useFetchAccountAssetsByIdQuery() and there is already a cache entry for fetchAccountAssetsById(undefined), so that is still being used - and no request is made.
If your component instead would be calling useFetchAccountAssetsByIdQuery(5) and it changes to useFetchAccountAssetsByIdQuery(6), RTK-query can safely identify that it has a cache entry for fetchAccountAssetsById(5), but not for fetchAccountAssetsById(6) and will make a new request, retrieving up-to-date information.
So, you should select that value in your component using useSelector and pass it into your query hook as an argument, not pull it out of the store in your query function.

Correct way to remove item from client side cache in apollo-client

I am using GraphQL with Apollo-Client in my React(Typescript) application with an in memory cache. The cache is updated on new items being added which works fine with no errors.
When items are removed a string is returned from GraphQL Apollo-Server backend stating the successful delete operation which initiates the update function to be called which reads the cache and then modifies it by filtering out the id of the item. This is performed using the mutation hook from Apollo-Client.
const [deleteBook] = useMutation<{ deleteBook: string }, DeleteBookProps>(DELETE_BOOK_MUTATION, {
variables: { id },
onError(error) {
console.log(error);
},
update(proxy) {
const bookCache = proxy.readQuery<{ getBooks: IBook[] }>({ query: GET_BOOKS_QUERY });
if (bookCache) {
proxy.writeQuery<IGetBooks>({
query: GET_BOOKS_QUERY,
data: { getBooks: bookCache.getBooks.filter((b) => b._id !== id) },
});
}
},
});
The function works and the frontend is updated with the correct items in cache, however the following error is displayed in the console:
Cache data may be lost when replacing the getBooks field of a Query object.
To address this problem (which is not a bug in Apollo Client), define a custom merge function for the Query.getBooks field, so InMemoryCache can safely merge these objects:
existing: [{"__ref":"Book:5f21280332de1d304485ae80"},{"__ref":"Book:5f212a1332de1d304485ae81"},{"__ref":"Book:5f212a6732de1d304485ae82"},{"__ref":"Book:5f212a9232de1d304485ae83"},{"__ref":"Book:5f21364832de1d304485ae84"},{"__ref":"Book:5f214e1932de1d304485ae85"},{"__ref":"Book:5f21595a32de1d304485ae88"},{"__ref":"Book:5f2166601f6a633ae482bae4"}]
incoming: [{"__ref":"Book:5f212a1332de1d304485ae81"},{"__ref":"Book:5f212a6732de1d304485ae82"},{"__ref":"Book:5f212a9232de1d304485ae83"},{"__ref":"Book:5f21364832de1d304485ae84"},{"__ref":"Book:5f214e1932de1d304485ae85"},{"__ref":"Book:5f21595a32de1d304485ae88"},{"__ref":"Book:5f2166601f6a633ae482bae4"}]
For more information about these options, please refer to the documentation:
* Ensuring entity objects have IDs: https://go.apollo.dev/c/generating-unique-identifiers
* Defining custom merge functions: https://go.apollo.dev/c/merging-non-normalized-objects
Is there a better way to update the cache so this error won't be received?
I too faced the exact same warning, and unfortunately didn't come up with a solution other than the one suggested here: https://go.apollo.dev/c/merging-non-normalized-objects
const client = new ApolloClient({
....
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
getBooks: {
merge(existing, incoming) {
return incoming;
},
},
},
},
}
}),
});
(I am not sure weather I wrote your fields and types correctly though, so you might change this code a bit)
Basically, the code above let's apollo client how to deal with mergeable data. In this case, I simply replace the old data with a new one.
I wonder though, if there's a better solution
I've also faced the same problem. I've come across a GitHub thread that offers two alternative solutions here.
The first is evicting what's in your cache before calling cache.writeQuery:
cache.evict({
// Often cache.evict will take an options.id property, but that's not necessary
// when evicting from the ROOT_QUERY object, as we're doing here.
fieldName: "notifications",
// No need to trigger a broadcast here, since writeQuery will take care of that.
broadcast: false,
});
In short this flushes your cache so your new data will be the new source of truth. There is no concern about losing your old data.
An alternative suggestion for the apollo-client v3 is posted further below in the same thread:
cache.modify({
fields: {
notifications(list, { readField }) {
return list.filter((n) => readField('id', n) !==id)
},
},
})
This way removes a lot of boilerplate so you don't need to use readQuery, evict, and writeQuery. The problem is that if you're running Typescript you'll run into some implementation issues. Under-the-hood the format used is InMemoryCache format instead of the usual GraphQL data. You'll be seeing Reference objects, types that aren't inferred, and other weird things.

Resources