How to get all cache data using reactjs #apollo/client v3 - react-apollo

Is there anyway that I can check all the cache, eg: changes within apollo for debugging.
Something like redux store, where you can view the whole state tree.
They mentioned:
The cache stores the objects by ID in a flat lookup table.
https://www.apollographql.com/docs/react/caching/cache-configuration/
Any way to display/console the whole lookup table?

For #apollo/client v3
Found the answer, if anyone is interested.
Through InMemoryCache
You can console log the cache object where you create with InMemoryCache.
You should be able to find it under your created cache:
const cache = new InMemoryCache({"...Your option"})
console.log(cache.data) // <- Your cache query
Through browser console
Through browser, use console to log data
__APOLLO_CLIENT__.cache.data
Through apollo v3
Access through apollo client cache
const client = useApolloClient();
const serializedState = client.cache.extract();
console.log(serializedState) <- your cache query

I just installed Apollo Client extension for Chrome, seem to work, there is now "Apollo" tab in the dev tools
https://chrome.google.com/webstore/detail/apollo-client-devtools/jdkknkkbebbapilgoeccciglkfbmbnfm?hl=en-US
I used "readQuery" method to get data from cache, might be not best way to do it.
import { useApolloClient, gql } from '#apollo/client';
...
const WEBSITE_TITLE = gql`
query GetSitewide {
sitewide {
data {
attributes {
header {
__typename
id
siteTitle
}
}
}
}
}
`;
...
function WebsiteTitle() {
const client = useApolloClient();
const {
sitewide: {
data: {
attributes: { header },
},
},
} = client.readQuery({
query: WEBSITE_TITLE,
});
const { siteTitle } = header;
return <> {siteTitle} </>;
}
export default WebsiteTitle;

Related

Query on page not refetching once navigating back to page

In my nextjs page I have the following hook (generated by using graphql-codegen) that fetches a graphql query.
const { data, error, loading, fetchMore, refetch, variables } = useGetShortlistQuery({
notifyOnNetworkStatusChange: true, // updates loading value
defaultOptions: {
variables: {
offset: undefined,
filterBy: undefined,
sortBy: SortBy.RecentlyAdded,
sortDirection: SortDirection.Desc,
},
},
});
This is the useGetShortlistQuery hook that is generated by graphql-codegen
export function useGetShortlistQuery(
baseOptions?: Apollo.QueryHookOptions<GetShortlistQuery, GetShortlistQueryVariables>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<GetShortlistQuery, GetShortlistQueryVariables>(GetShortlistDocument, options);
}
my component is wrapped in a HOC to enable Apollo Client
export default withApollo({ ssr: true })(Index);
The withApollo HOC uses #apollo/client and the cache property of the apollo client is as follows.
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
getShortlist: {
keyArgs: [],
merge(existing: PaginatedProperties | undefined, incoming: PaginatedProperties): PaginatedProperties {
return {
...incoming,
properties: [...(existing?.properties || []), ...(incoming?.properties || [])],
};
},
},
},
},
},
}),
The problem I am having is that on this page I update the variables on the useGetShortlistQuery using refetch which, in turn, updates the data.
However, if I navigate to another page, then come back to this page using this component. It doesn't seem to retrigger the graphql query so returns the previous data.
If you are using getStaticProps (or getServerSideProps) with pre rendered pages, it is a known behavior. It is due to optimisation by Next.js not re-rendering components between page navigations, with pages like [id].js.
The trick is to have a key on components that you want to see refreshing. You have multiple ways to do so. Having a different key on components tells React that it should be re-rendering the components, and thus will trigger again the hooks.
Practical example:
export const getStaticProps: GetStaticProps = async ({ params }) => {
const data = getData() //something that fetches your data here
return {
props: {
// some other data you want to return...
// some unique value that will be on each page
key: data.key
},
}
}
const MyPage: NextPage<InferGetStaticPropsType<typeof getStaticProps>> = (props) => {
<div key={props.key} />
}

Apollo-client State management cache is not updating automatically after save and delete operation

I am using apollo-client for graphql calls along with I added state management with in the package apollo-client. In graphql module, assigned InMemoryCache to cache variable and client variable is exported.Client variable is imported in component so data is available in client.cache.data after default get call executed but I want to update client cache after save and delete graphql operations success callbacks
Here is my graphql.module.ts:
import {NgModule} from '#angular/core';
import {ApolloModule, APOLLO_OPTIONS} from 'apollo-angular';
import { ApolloClient } from 'apollo-client';
import {InMemoryCache} from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
const cache = new InMemoryCache();
const link = new HttpLink({
uri:'https://api.graph.cool/simple/v1/ciyz901en4j590185wkmexyex'
});
export var client = new ApolloClient({
cache,
link
});
#NgModule({
})
and my service call implementation
client
.query({
query: gql`
{
fORoomTypes()
{
nodes
{
roomTypeId
roomType
ratSingle
ratDouble
ratExtra
statu
}
}
}
`,
})
.then(result => {
callback(result);
});
after callback client.cache.data contain data, I want to call this data with cache queries and I want to update cache automatically
this is my save service implementation
const post = gql`
mutation
{
saveRtype(rt:
{
rty:
{
rt:"Club room"
rat:4000
cchub:4500
ext:800
statu:1
}
traCheck:1
}
)
}
`
client.mutate({
mutation: post
}).then((data) => {
callback(data)
});
I've only used apollo client with React but hopefully can shed light on how it works. For this to happen "automatically", you either need to call refetchQueries to refetch fORoomTypes after the mutation, or manually update the cache. I noticed your responses do not return an id property, so how would it know which one to update? If there is an identifier that isn't called "id", then register it following this documentation.
Here is the link to documentation in general for what you want
https://www.apollographql.com/docs/angular/features/cache-updates/

How can I use Apollo's cacheRedirect with a nested query

I've got a query that looks like this:
export const GET_PROJECT = gql`
query GetProject($id: String!) {
homework {
getProject(id: $id) {
...ProjectFields
}
}
}
${ProjectFieldsFragment}
`;
My InMemoryCache looks like this:
const cache = new InMemoryCache({
dataIdFromObject: ({ id }) => id,
cacheRedirects: {
Query: {
getProject: (_, args, obj) => {
console.log('Hello world');
},
},
}
});
The above cache redirect is never hit. However, if I modify it to look like:
const cache = new InMemoryCache({
dataIdFromObject: ({ id }) => id,
cacheRedirects: {
Query: {
homework: (_, args, obj) => {
console.log('Hello world');
},
},
}
});
It does get hit, however I don't have any of the arguments that are passed in the nested getProject query. What's also confusing is that this cache redirect function is hit for queries that it seemingly shouldn't get hit for, like:
export const SESSION = gql`
query Session {
session {
user {
id
fullName
email
}
organizations {
name
id
}
}
}
`;
So what is going on? I've resorted to just using readFragment in the places where I want the cache to redirect, but I'd like for that logic to become centralized.
It's hard to say for sure with these kinds of issues, but I'm betting that, since you say
What's also confusing is that this cache redirect function is hit for queries that it seemingly shouldn't get hit for
the issue might be with your dataIdFromObject function.
This function is ultimately what decides if data is read from the cache or not. You should only override this if you have a very specific reason to. For example:
import { InMemoryCache, defaultDataIdFromObject } from 'apollo-cache-inmemory';
// ...
export default new ApolloClient({
link,
cache: new InMemoryCache({
dataIdFromObject(object) {
switch (object.__typename) {
case 'ModifierScale':
case 'ModifierGroup':
return [
object.__typename,
object.id,
...object.defaults
.map((defaultModifier) => defaultModifier.id)
.join(''),
].join('');
default:
return defaultDataIdFromObject(object); // fall back to default handling
}
},
}),
});
The point of this setting is to allow you to customize the key that gets put into the cache when you are loading the data.
If this doesn't solve your issue, I would definitely go into the Apollo tab in the chrome dev tools (you need the Apollo dev tools chrome extension to do this) and look at the cache section. It should show you the data in the cache and the key that the data is stored in.

Apollo useQuery() - "refetch" is ignored if the response is the same

I am trying to use Apollo-client to pull my users info and stuck with this problem:
I have this Container component responsible for pulling the user's data (not authentication) once it is rendered. User may be logged in or not, the query returns either viewer = null or viewer = {...usersProps}.
Container makes the request const { data, refetch } = useQuery<Viewer>(VIEWER);, successfully receives the response and saves it in the data property that I use to read .viewer from and set it as my current user.
Then the user can log-out, once they do that I clear the Container's user property setUser(undefined) (not showed in the code below, not important).
The problem occurred when I try to re-login: Call of refetch triggers the graphql http request but since it returns the same data that was returned during the previous initial login - useQuery() ignores it and does not update data. Well, technically there could not be an update, the data is the same. So my code setUser(viewer); does not getting executed for second time and user stucks on the login page.
const { data, refetch } = useQuery<Viewer>(VIEWER);
const viewer = data && data.viewer;
useEffect(() => {
if (viewer) {
setUser(viewer);
}
}, [ viewer ]);
That query with the same response ignore almost makes sense, so I tried different approach, with callbacks:
const { refetch } = useQuery<Viewer>(VIEWER, {
onCompleted: data => {
if (data.viewer) {
setUser(data.viewer);
}
}
});
Here I would totally expect Apollo to call the onCompleted callback, with the same data or not... but it does not do that. So I am kinda stuck with this - how do I make Apollo to react on my query's refetch so I could re-populate user in my Container's state?
This is a scenario where apollo's caches come handy.
Client
import { resolvers, typeDefs } from './resolvers';
let cache = new InMemoryCache()
const client = new ApolloClient({
cache,
link: new HttpLink({
uri: 'http://localhost:4000/graphql',
headers: {
authorization: localStorage.getItem('token'),
},
}),
typeDefs,
resolvers,
});
cache.writeData({
data: {
isLoggedIn: !!localStorage.getItem('token'),
cartItems: [],
},
})
LoginPage
const IS_LOGGED_IN = gql`
query IsUserLoggedIn {
isLoggedIn #client
}
`;
function IsLoggedIn() {
const { data } = useQuery(IS_LOGGED_IN);
return data.isLoggedIn ? <Pages /> : <Login />;
}
onLogin
function Login() {
const { data, refetch } = useQuery(LOGIN_QUERY);
let viewer = data && data.viewer
if (viewer){
localStorage.setItem('token',viewer.token)
}
// rest of the stuff
}
onLogout
onLogout={() => {
client.writeData({ data: { isLoggedIn: false } });
localStorage.clear();
}}
For more information regarding management of local state. Check this out.
Hope this helps!

Apollo GraphQL: Lazy loading data after SSR

I have the following code:
const HomeWithApollo = withApollo(compose(
graphql(HOME_QUERY, {
props({
data: { loading, page, fetchMore }
}) {
return {
loading,
page,
fetchLocations: () => {
return fetchMore({
query: ALL_LOCATIONS_QUERY,
updateQuery: (prev, {fetchMoreResult}) => {
if (!fetchMoreResult.data) { return prev; }
return {
data: [...prev, ...fetchMoreResult.data]
};
}
})
}
};
}
})
)(Home));
Before migrating to Apollo I loaded the ALL_LOCATIONS_QUERY as an isomorphic fetch client-side (basically an AJAX request). But I'm looking for the Apollo way and I'm not sure if I have it yet. I have a few questions.
Using graphql(QUERY_NAME, { options }), since I'm loading Home data and Locations data completely separately, should they be placed in separate, multiple graphql functions within the withApollo(compose([...here]) Higher Order Component?
Currently to get data from the fetchMore function I am doing the following, but something tells me that it should be done within state so Apollo is aware of it for caching. Any thoughts on this? Am I moving in the right direction?
async componentDidMount(){
const { data } = await this.props.fetchLocations();
this.setState({ locations: data.allLocations });
}
Thank you very much in advance!

Resources