Let's say I have a simple data model like this
User
- email
- password
- Profile
- profile_image
- address
- phone_number
When I visit the user's profile page, I use useQuery and query user from server
const ME = gql`
query {
me {
email
profile {
profileImage
address
phoneNumber
}
}
`;
const {loading, data, refetch} = useQuery(ME);
And when I want to update a profile. I will do this
const UPDATE_PROFILE = gql`
mutation($profileImage: String!, $address: String!, $phoneNumber: String!) {
updateProfile(profileImage: $profileImage, address: $address, phoneNumber: $phoneNumber) {
profileImage
address
phoneNumber
}
}
`;
const [updateProfile, {loading}] = useMutation(UPDATE_PROFILE, {
onCompleted(data) {
// Refetch to refresh whole user data
refetch();
}
}
I just want to display new updated user info in the page, So What I do is calling refetch() from useQuery(ME).
But I found that I can use refetchQueries() from this doc.
Which will be a better choice? What is the difference between them?
The difference between refetchQueries and refetch:
refetchQueries: you can refetch any queries after a mutation including your ME query or other queries like getMessageList, getListUser,...
refetchQueries is the simplest way of updating the cache. With refetchQueries you can specify one or more queries that you want to run after a mutation is completed in order to refetch the parts of the store that may have been affected by the mutation (refetchQueries doc).
refetch: you just can refetch query ME when your use refetch which is one of the results of useQuery(ME).
A function that allows you to refetch the query and optionally pass in new variables (refetch doc).
In your case, if you want to refetch your ME data, you can use refetch. On the other hand, if your want to update other queries you should use refetchQueries.
In my experience, I prefer using refetchQueries after a mutation to using refetch.
Related
I've a bit of confusion how to perform multiple mutation in graphql.
I've read articles about using of graphql() function or compose() function, but i haven't understood if these are the legacy way to do it (version 2)
or is still a valid way to proceed.
I'm currently using apollo 3.3 with grandstack and in my component i do the following to perform a mutation (i used useMutation() hook provided by ApolloClient)
const CreateFBUser = gql `
mutation CreateFBUser($name: String, $fbId: String!) {
CreateFBUser(name: $name, fbId: $fbId) {
_id
id
fbId
name
}
}
`
function FbUserForm() {
const { id } = useParams()
const [formState, setFormState] = useState({})
const [createFBUser,{data: createData}] = useMutation(CreateFBUser)
const { loading, error, data } = useQuery(FBUser,{
variables: {_id: id}
});
...
..
.
as you can see, i havent used components like <Query>, and so on..
FIRST QUESTION: is this component related to the apollo old version? are still regulary used or useMutation() hook is the first choice in apollo 3?
SECOND QUESTION: i need to perform a second mutation related to the first, and i need the generated id from the first mutation to execute the second
//the first mutation
const CreateFBUser = gql `
mutation CreateFBUser($name: String, $fbId: String!) {
CreateFBUser(name: $name, fbId: $fbId) {
_id
id
fbId
name
}
}
`
//the second mutation (pseudocode)
const AddFBUserMemberOf = gql`
mutation AddFBUserMemberOf($from: _GroupInput!, $to: _FBUserInput!) {
AddFBUserMemberOf(from: $from, to: $to) {
from
to
}
}
`
moreover, the second mutation should be performed conditionally according to a value/a variable/something else
The render prop components are deprecated and will not receive further updates or bug fixes according to the docs
For your second question; the mutation function returned from useMutation takes an onCompleted property in the options parameter that executes after the mutation successfully completes.
const [createFBUser,{data: createData}] = useMutation(CreateFBUser)
const [addFBUserMemberOf] = useMutation(AddFBUserMemberOf)
createFBUser({
variables: {
//...
},
onCompleted: (data) => {
// data contains the result of createFBUser
addFBUserMemberOf({
variables: {
//...
}
})
}
})
time for a new question. Each page as a query simular like:
const {data} = useQuery(EXAMPLE)
export const EXAMPLE = gql`
query homePage(
$orderBy: PostOrderByInput
$userId: ID
) {
posts( //Individual query for each page
orderBy: $orderBy
userId: $userId
) {
id
title
}
userUserRelation{ //alway the same query. Need this request just once
id
}
}
`;
The posts query is individual. Always different variables for each page. The userUserRelation query is alway the same. I just need the userUserRelation once from the db, at the first visited page. Then I can query it from the cache.
Is there a possibility to make the posts request always from the db and the userUserRelation query just once from the db and then from the cache? Comparable like #client
Of course I can solve this with a global state. And make a extra gql without the userUserRelation after the fist request. Or I make a extra useQuery (But then I have 2 queries the fitst time...)
THX for any help.
You should split your query into two separate hooks. That way userUserRelation will only be fetched once as long as you use the default fetchPolicy of cache-first.
export const POSTS_QUERY = gql`
query Posts(
$orderBy: PostOrderByInput
$userId: ID
) {
posts(orderBy: $orderBy, userId: $userId) {
id
title
}
userUserRelation {
id
}
}
`;
export const USER_QUERY = gql`
query User {
userUserRelation {
id
}
}
`;
const { data: userData } = useQuery(USER_QUERY)
const { data: postsData } = useQuery(POSTS_QUERY, { variables: { ... } })
If you want the queries to be fetched inside a single request initially, you can enable batching for your client.
I am working on my first GraphQL React Client app and I am not sure about some best practices.
I have a query, that gets me places from an API to be shown on a map.
In the map.js component I fetch the data with this query:
const GET_PLACES_ON_MAP = gql`
query Region($input: MapInput!) {
map(input: $input) {
places {
id
name
distance
long
lat
}
infos {
totalPlacesInArea
responseTime
}
}
}
`;
const { loading, error, data } = useQuery(GET_PLACES_ON_MAP, someQueryParams);
The response includes infos about the total number of places and the response time of API response.
This infos object should be used in other components too (e.g. in some kind of dashboard) and it changes everytime a new query is made in the map component.
My question no 1:
What's the best practice to use infos in another component that do not query the API itselves?
My first try was to save infos in a local state graphql object like with the following mutation and resolver code (this sets the infos in local state but I get endless rendering of the map component):
const SET_API_INFO = gql`
mutation {
setApiInfo(params: $params) #client
}
`;
const GET_API_INFO = gql`
{
apiInfo #client {
totalPlacesInArea
responseTime
}
}
`;
const resolvers = {
Mutation: {
setApiInfo: (_root, variables, { cache, getCacheKey }) => {
const { apiInfo } = cache.readQuery({ query: GET_API_INFO });
const { params } = variables;
_.map(params, (value, key) => {
apiInfo[key] = value;
});
cache.writeData({ data: { apiInfo } });
},
},
};
My question no 2:
I was looking for some tutorial or an open source project that uses Apollo Client & React to learn more about the architecture of a really complex app. Is there anywhere a good example that goes further then query, mutation, subscription?
You don't need to store your data in the client cache because Apollo supports fetchPolicy. When you use the same query with the same variables, query fields in any component, Apollo will look up its cache query first before making a new network request. If the query has existed, Apollo will return the last query result.
By default, Apollo Client's fetch policy is cache-first, which means it checks the cache to see if the result is there before making a network request. Since we want this list to always reflect the newest data from our graph API, we set the fetchPolicy for this query to network-only.
I don't know any Apollo complex app, but you can read more about Apollo's best practices in Apollo blog.
So I have this Apollo Query Component like this:
<Query
fetchPolicy='network-only' // also tried without and with 'no-cache'
query={GET_MENUS}
variables={{
foo // This has the default value of the state
}}
>
{({ loading, error, data, refetch }) => {
// Display Data here
// We have an Imput here that can change the State of Bar in the parent Component
<Button
onPress={() => {
/*refetch({
foo: { bar}
}); */
setBar(blubb); // I am using react hooks (useState)
}}
text='Refresh!'
/>
}
)}
</Query>
I tried to refetch by using the refetch method and also by just updating the state. Actually I checked the Apollo Server and in both methods the new variables get passed, but the new Data is not updated. The funny thing is, that if I just use another default value in the state, it works fine. I also tried different fetch-policies without any luck.
I thought it should be quite basic, but I didn't find any solution so far...
So how do I get data with my new variables?
EDIT:
GET_MENUS is a bit complicated, but this is the whole thing. I am passing the variables into different resolvers, because they are nested. The Foo Bar thingy is the "daily" variable
const GET_MENUS = gql`
query getMenus($lat: Float!, $lng: Float!, $daily: Daily) {
getMenus(lat: $lat, lng: $lng) {
distance
location {
_id
street
streetNumber
plz
city
coordinates
shopIDs {
name
togo
shopType
menus(daily: $daily) {
_id
name
price
hot
sweet
togo
allergies
components
}
}
}
}
}
`;
My solution to refetch using variables in Apollo 3.0:
import { gql, useApolloClient } from "#apollo/client";
const client = useApolloClient();
const SEARCH_PROJECTS = gql``
await client.query({
query: SEARCH_PROJECTS,
variables: { page: 1, limit: 1 },
notifyOnNetworkStatusChange: true,
fetchPolicy: "network-only"
})
See more about the fetch policy here and here.
My context was the following: I fetch a list of projects, then the user can remove or update the projects. The list of projects, the project, the update and delete are different components. The default refresh provided by Apollo doesn't allow me to send the variables for the project' pagination, so when I remove or update a project I refresh it manually, without the need to create a structure where I can use the refresh or fetch more option from the component "list of projects"
I have a lot of active queries stored in my Apollo Cache, for example:
items(isPublished: true, orderBy: "name", filterByName: "")
items(isPublished: true, orderBy: "name", filterByName: "home")
items(isPublished: false, orderBy: "name", filterByName: "home")
items(isPublished: true, orderBy: "age", filterByName: "home")
...
and, consequently, a lot of possible variables for the same query (GET_ITEMS), with more and more filters. When I want to add, move or remove an item, I update the Apollo Cache with the propery update of Mutation component, for example:
import gql from "graphql-tag";
import { Mutation } from "react-apollo";
const ADD_ITEM = gql`
mutation AddItem(...
`;
...
<Mutation mutation={ADD_ITEM} variables={...} update={() => ...} />
But, if I want well updated all my cached queries ... how I accomplish this? Would I have to cache.readQuery and cache.writeQuery inside update function for each query? That would be madness for me.
I'm lost with this. Thanks in advance.
This is one of the unfortunate limitations of ApolloClient, but can be solved by utilizing apollo-link-watched-mutation. The Link allows you to relate mutations and queries by operation name, such that for any mutation with a specific operation name, all queries with a particular operation name can be updated. The biggest downside to this approach is that it moves the update logic outside of your component and into your client configuration, which may obfuscate things a bit.
Example usage, given a mutation named AddItem and queries named Items:
const cache = new InMemoryCache()
const link = new WatchedMutationLink(cache, {
AddItem: {
Items: ({ mutation, query }) => {
const addedItem = mutation.result.data.addItem
const items = query.result.items
const items.push(addedItem)
return {
...query.result,
items,
}
}
}
})
Note that you can inspect both the query and the mutation passed to the function to determine what, if anything, needs to be changed. Your actual code may be different based on what your queries actually look like, this is an example. See the docs for additional details.