Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refetchQueries after mutation #1900

Closed
tokenvolt opened this issue Jul 14, 2017 · 51 comments
Closed

refetchQueries after mutation #1900

tokenvolt opened this issue Jul 14, 2017 · 51 comments

Comments

@tokenvolt
Copy link

tokenvolt commented Jul 14, 2017

Intended outcome:

I'm trying to refetch some queries after the mutation and I expect them to be executed.

Actual outcome:

refetchQueries are not working as they are described in the docs

How to reproduce the issue:

mutate({
  mutation: LOGIN_MUTATION,
  variables: {
    ...values
  },
  refetchQueries: [USER_QUERY]
})

LOGIN_MUTATION and USER_QUERY are strings in graphql syntax. I don't see USER_QUERY gets executed after the mutation.

@n1ru4l
Copy link
Contributor

n1ru4l commented Jul 14, 2017

From the documentation:

A list of query names which will be refetched once this mutation has

Also you will have to convert your LOGIN_MUTATION graphql strings to a GraphQL AST by using graphql-tag

It should look like this:

import gql from 'graphql-tag'

const USER_QUERY = gql`
  query currentUser {
    id
    login
  }
`

const LOGIN_MUTATION = gql`
  mutation login {
    login(login: "foobars")
  }
`
[...]

mutate({
  mutation: LOGIN_MUTATION,
  variables: {
    ...values
  },
  refetchQueries: [`currentUser`]
})

@tokenvolt
Copy link
Author

I'm using graphql-loader and writing mutations and queries in separate graphql files. How should I approach this way? BTW I've tried to use gql and use the name of the query, but it doesn't work as well.

@n1ru4l
Copy link
Contributor

n1ru4l commented Jul 14, 2017

then you should probably create a reproduction with https://github.com/apollographql/react-apollo-error-template . I never used refetchQueries so my knowledge is limited to the documentation 😞

@lucfranken
Copy link

See also: #1697 where I tried to figure out the same problem. Did not find a really good solution yet but it may give you some insight in the options.

@jbaxleyiii
Copy link
Contributor

@tokenvolt I think this may be a shape issue within refetchQueries. Can you try

refetchQueries: [
{ query: USER_QUERY }
]

The object is needed in order to support things like variable changes

@tokenvolt
Copy link
Author

@jbaxleyiii still no luck

@jbaxleyiii
Copy link
Contributor

@tokenvolt do you think you could create a simple reproduction for this? We have an error template and code sandbox listed in the issue template (try to create a new issue and it should be there) or a failing test?

@tokenvolt
Copy link
Author

@jbaxleyiii I'll try to reproduce

@ctavan
Copy link

ctavan commented Jul 17, 2017

@tokenvolt FYI I was also not lucky with using the query-name based variant of refetchQueries. While it should work as expected there currently seems to be a race-condition which effectively prevents it from working, see #1821

In principle though, the query-name based refetchQueries is an expected feature, here's the implementation:

  • if (typeof refetchQueries[0] === 'string') {
    (refetchQueries as string[]).forEach(name => {
    this.refetchQueryByName(name);
    });
    } else {
  • // Refetches a query given that query's name. Refetches
    // all ObservableQuery instances associated with the query name.
    private refetchQueryByName(queryName: string) {
    const refetchedQueries = this.queryIdsByName[queryName];
    // Warn if the query named does not exist (misnamed, or merely not yet fetched)
    if (refetchedQueries === undefined) {
    console.warn(
    `Warning: unknown query with name ${queryName} asked to refetch`,
    );
    return;
    } else {
    return Promise.all(
    refetchedQueries.map(queryId =>
    this.observableQueries[queryId].observableQuery.refetch(),
    ),
    );
    }
    }

@tokenvolt
Copy link
Author

tokenvolt commented Jul 25, 2017

Hey, guys, I figured out that it was an issue in my application. Sorry for bothering, as it was just a stupid mistake on my side. refetchQueries works as it was mentioned in #1900 (comment).

But I am too experiencing a race condition #1821

@stale
Copy link

stale bot commented Aug 16, 2017

This issue has been automatically marked as stale becuase it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client!

@stale stale bot closed this as completed Aug 30, 2017
@stale
Copy link

stale bot commented Aug 30, 2017

This issue has been automatically closed because it has not had recent activity after being marked as stale. If you belive this issue is still a problem or should be reopened, please reopen it! Thank you for your contributions to Apollo Client!

@abraga
Copy link

abraga commented Dec 7, 2017

For those who are still having this problem, try putting refetchQueries inside options:

name: '...',
options: {
  refetchQueries: [USER_QUERY]
}

@tsvetann
Copy link

@abraga still doesn't work for me

@SebastianMuniz
Copy link

Hey I'm experiencing same problme, any solutions?? none of the things mentioned above solved the issue. Everything is working fine, but refetchQueries

@wesbos
Copy link

wesbos commented Feb 7, 2018

Same issue here - I can manually call .refetch(), but this doesn't seem to trigger

@SebastianMuniz
Copy link

@wesbos Hi Wes how you doing? I had to use a workaround here, I imagine you are using a state container(Redux, Mobx), so you have the records collection in your store and every time you make a mutation, select all the objects fields, once the response comeback you can use that data to merge it into your existing records collection. Do you catch the idea?

@wesbos
Copy link

wesbos commented Feb 7, 2018

I'm not using any sort of state container outside of Apollo - so I'm not sure this applies

@yellowspaceboots
Copy link

I was having a similar issue trying to refetch a query after my login mutation. But I placed some console.logs and it looks like in my issue it may just be an async issue. My mutation is firing first but the query is firing before my result gets back so it makes the query i am firing appear to have no change in my UI. I tried simply wrapping my resolver in async/await but of course it was not that easy :P

@wesbos
Copy link

wesbos commented Feb 8, 2018

So did you figure it out?

@yellowspaceboots
Copy link

yellowspaceboots commented Feb 8, 2018

@wesbos My query is taking my userId through apollo's context object. I believe that the query is firing before I can set the context so it still sees context.userId as undefined and so my query appears to be the same as when my user was logged out. If I run the query again it updates as it is supposed to because now that my user is logged in the context is set. I am in the middle of trying to figure it out but I believe apollo-link-context is what I am looking for but no I have not solved it yet.

I am not sure if you are using context or not but my refetch works fine otherwise so I would say this may be an async issue for you as well but I am in the same boat as you so who knows :P

@yellowspaceboots
Copy link

So I ended up solving my issue for the most part. I needed to wrap my login resolver to return a Promise. What was happening is my context was being updated before I received my login token. If your refetch queries is firing before your login process is finished than the query will appear to not change. I am not sure if this helps but I wanted to share what I found.

@markzrt
Copy link

markzrt commented Feb 13, 2018

works well when you use optimisticResponse :(

@Cabezaa
Copy link

Cabezaa commented Feb 15, 2018

after a while, i used the reply of @abraga like a started point, and search in other issiues and found that, the way that i be able to solve this problem was

the code that @abraga supply was

name: '...',
options: {
   refetchQueries: [USER_QUERY]
}

Instead, I used:

name: '...',
options: {
   refetchQueries: [{query: USER_QUERY}]
}

I'm using "withApollo" HOC and graphql as an decorator.

@mariomelo
Copy link

Guys, just a heads-up:

When using React, if you call this.props.mutate({ options: {refetchQueries: [ 'queryToRefetch']}}) it doesn't work. However, if you pass the options parameter to the graphql function it works.

...

export default graphql( my_mutation, { name: 'my_mutation', options: {refetchQueries: [ 'queryToRefetch']} } ) ( MyComponent)

@Aetherall
Copy link

Use a Map to pass the limitation

https://github.com/apollographql/react-apollo/blob/4285679f59af2c5b9e6ba0076c9aaabe30f50a2f/src/Mutation.tsx#L196

mutate({
  .........,
  refetchQueries: new Map([[0,'todolist'],[1, 'todogroups']])
})

function bypass(...queryNames){
  return new Map(queryNames.map((name, i) => [i, name])
}

mutate({
  .........,
  refetchQueries: bypass('todolist', 'todogroups')
})

@waclock
Copy link

waclock commented Jun 19, 2018

I'm having an issue re-rendering the component.

1- I have an index showing basically all my bank accounts (it uses bank account query)
2- I have a modal, which is a form and adds a new bank account. When the modal is open, the index is in the back.
3. When the mutation adding a new bank account is done, refetchQueries is called (I can see in the network tab the query is done) and also the store is updated (I can see so looking at the apollo devtools)
4. Even though the store is updated and the query is executed, the "index" of bank accounts is not updated. I'm not sure what could be the problem.

@wzup
Copy link

wzup commented Aug 20, 2018

@n1mmy

A list of query names which will be refetched once this mutation has

This is absolutely not obvious what is expected.
As @tokenvolt I also provided a list of gql(...) tags and not what you write.

Their documentation is miserable.

@wzup
Copy link

wzup commented Aug 20, 2018

@wzup
Copy link

wzup commented Aug 20, 2018

@abraga

try putting refetchQueries inside options:

What options are you talking about? The options you provide are the only options. You can't have options.options.
Read https://www.apollographql.com/docs/react/api/apollo-client.html#ApolloClient.mutate

How can you specify options here?

    this.props.client.mutate({
      mutation: CreatePatientMutation,
      refetchQueries: ['getPatientDetailsQuery'],
      options: ???? // HOW?
      variables: {
        foo: 'bar'
      }})
        .then(res => {
        })
        .catch(err => {
        });

@smeijer
Copy link

smeijer commented Sep 20, 2018

Experiencing a similar situation as @waclock. I see that new data is being fetched, but the component depending on that query doesn't update.

@saeta-eth
Copy link

Hi guys!
I solved this problem with the following code:

<Mutation
  mutation={REMOVE_USER}
  variables={{ userId: user.id }}
  refetchQueries={[{ query: GET_USERS }]}  // <--- HERE
>
  {removeUser => (
    <button onClick={removeUser}>Remove User</button>
  )}
</Mutation>

Hope it helps.

@borisyordanov
Copy link

borisyordanov commented Nov 8, 2018

FYI if the solution @slorenzo posted isn't working try this

@YCMitch
Copy link

YCMitch commented Apr 12, 2019

So far there's been like 8 different methods posted, most of which have never worked for me. It's completely ridiculous that it's such a game of whispers (does it change in every version?).

FWIW, in my case I was using compose to add my mutations to props, so I'm going:

this.props.upsertPerson({
    variables {
          payload: {
             ...fields
         }
    },
    refetchQueries: () => [{ query: GET_PERSON, variables: {id: personId} }]
})

That works for me. None of the other posted solutions did anything.

@cihadturhan
Copy link

This is complete nonsense and yes @MitchEff 's solution works:

await client.mutate({
        mutation: COMPLETE_CHALLENGE,
        variables: data,
        refetchQueries: () => [{query: UNCOMPLETED_CHALLENGES}]
})

@joseffb-mla
Copy link

joseffb-mla commented Apr 23, 2019

I get error back of You must wrap the query string in a "gql" tag. when I add the query name.

refreshQuery = [{
        query: ['GetCollectionByUserId'],
        variables: { userId : userId, first : 100, last : 0, cursor : "" },
    }] ```

adding the query itself does nothing.

How do I wrap the query in gql statement if it's a query name?

The query is stored in a file and imported into a constant on first run.

@joseffb-mla
Copy link

joseffb-mla commented Apr 23, 2019

ok so passing the entire query seems to be accepted (no error) but also my data is still old. is there another promise I need to process on to wait for the second query to process before I pass to he result to the calling method?

query: gql`${CollectionsByUserID}`,

right now I do mutate {blah blah).then(stuff) is there now a new var passed to then to tell me when the refresh has occurred or will it only pass me stuff for the then after it's mutated AND refreshed the cache. Thanks for any help.

Update: it seems to work after the second call (the first call looks like nothing happens, but click update again and it works fine.) Any advice , as it always seems one record behind.?

@joseffb-mla
Copy link

Solved: had to add awaitRefetchQueries = true to my options array.

@tahmidrahman-dsi
Copy link

This worked for me. Thanks to @slorenzo 's solution

client.mutate({ mutation, variables, refetchQueries: [ { query: ...name, variables: { ...queryVariables } } ] })

@roelzkie15
Copy link

roelzkie15 commented Oct 17, 2019

Any idea how do we get the response data from refetchQueries? I got the query response data from mutation.

Mutation
import { gql } from 'apollo-boost';

export const DONE_TASK = gql`
    mutation DoneTask($taskId: ID!) {
        doneTask(input: {
            taskId: $taskId
        }) {
            task {
                id
                status
            }
        }
    }
`;
Query
import { gql } from 'apollo-boost';

export const GET_TASKS_BY_STATUS = gql`
    query GetTasksByStatus($status: String!) {
        getTasksByStatus(status: $status) {
            edges {
                node {
                    id
                    status
                    description
                }
            }
        }
    }
`;
Usage
const response = await client.mutate({
    mutation: DONE_TASK,
    variables: {
        taskId: 1
    },
    refetchQueries: () => [{
        query: GET_TASKS_BY_STATUS,
        variables: { 
            status: "OPEN"
        },
    }]
});

console.log(response);
Output
data: {
    doneTask: {
        task: { id:  1, status: 'DONE'}
    }
}

But I expect a response data from GET_TASKS_BY_STATUS. 🤔 😕

@tayloraucoin
Copy link

Still experiencing this issue. I've tried the solutions listed with no success. Will be diving into the race issue listed as I have yet to explore that potential.

Here is where I'm at with no luck:

// NOTHING UPDATES on session update
function App({ apolloClient }) {
    const { loading, error, data } = useQuery(GET_CURRENT_SESSION);

    if (loading) return null;
    if (error) console.log('error', error);
    const { session } = data;
    console.log('app session', session);  // NO UPDATE

    return (
        <Router history={history}>
            ...
            <Authentication/>
            ...
        </Router>
    );
}

// UPDATES on session.isLoading update
function Authentication(props) {
    ....
    updateSessionLoading({ 
        variables: { loading: true },
        options: {
            refetchQueries: () => [{ query: GET_CURRENT_SESSION }],
            awaitRefetchQueries: true
         }
    });
    ....
    return (
        ...
    )
}

@nik-lampe
Copy link

nik-lampe commented Jan 6, 2020

Maybe this helps:

When I'm loading the query from a .graphql file via graphql.macro the refetch doesn't work.
But when I'm declaring it with gql, it works just fine.


const [mutate] = useMutation(ASSIGN_QUESTIONNAIRE_FOR_USER, {
    variables: { userId: clientId },
    refetchQueries: ['questionnaires'],
    awaitRefetchQueries: true,
})

await mutate({
    variables: { templateId: id },
})
export const QUESTIONNAIRES_LIST = gql`
  query questionnaires($userId: ID) {
    questionnaires(userId: $userId) {
      id
      type
      date
      finished
      user {
        firstName
        lastName
      }
    }
  }
`

I wanted to recreate it with codesandbox but I can't find any public graphQL API, where I can write data.

@hbw3
Copy link

hbw3 commented Mar 21, 2020

This is complete nonsense and yes @MitchEff 's solution works:

await client.mutate({
        mutation: COMPLETE_CHALLENGE,
        variables: data,
        refetchQueries: () => [{query: UNCOMPLETED_CHALLENGES}]
})

How did you get "refetchQueries" response

@wosephjeber
Copy link

I've been wrestling with getting the Apollo client to wait for the queries specified in refetchQueries to resolve before resolving the mutation promise and wanted to chip in with something I just learned in case it helps anyone still struggling with this.

For one, you need the awaitRefetchQueries option to be set to true on the mutation. But even with that set, it seems that the fetchPolicy of the watched query that you're refetching makes a difference if you're specifying those refetchQueries by strings.

For example, given a mutation a call like this:

await client.mutate({
  mutation: MUTATION,
  variables: data,
  refetchQueries: ['QueryName'],
  awaitRefetchQueries: true
})

The client.mutate promise will resolve too soon if that query was called with the cache-and-network fetch policy. But if that query uses cache-first or network-only, I'm seeing that the mutation promise waits until the refetch query resolves, as expected.

@MAzeem6778
Copy link

@tokenvolt I think this may be a shape issue within refetchQueries. Can you try

refetchQueries: [
{ query: USER_QUERY }
]

The object is needed in order to support things like variable changes

worked.

@ghost
Copy link

ghost commented Sep 26, 2020

My solution to refetch using variables on Apollo 3.0:

import { gql, useApolloClient } from "@apollo/client";

const client = useApolloClient();

const SEARCH_PROJECTS = gql``

const removeProject = async () => {
         // Your mutation or query here

         const projects = (await client.query({
                    query: SEARCH_PROJECTS,
                    variables: { page: 1, limit: 1 },
                    notifyOnNetworkStatusChange: true,
                    fetchPolicy: "network-only"
          })).data.searchProject
}

See more about the fetch policy here.

@Cabezaa
Copy link

Cabezaa commented Sep 28, 2020

This is complete nonsense and yes @MitchEff 's solution works:

await client.mutate({
        mutation: COMPLETE_CHALLENGE,
        variables: data,
        refetchQueries: () => [{query: UNCOMPLETED_CHALLENGES}]
})

How did you get "refetchQueries" response

Hi @hbw3 , sorry for the big delay, but i hope this post helps other people that have the same question as you.

All refetch queries are managed as normal queries, so we have access to all methods as if we are calling a individual one. You can check this in the documetation.

So, a example for this is:

useMutation(MUTATION_GQL, {
    variables: {
        id: variable_mutation,
    },
    refetchQueries: [
        {
            query: QUERY_1,
            variables: {
                id: variable1,
                some: variable2,
            },
            fetchPolicy: 'network-only',
            onCompleted: data => {
               console.log(data)
            },
            onError: data => {
               console.error(data)
            },
        },
        {
            query: QUERY_2,
            variables: {
                id: variable3,
            },
        },
    ],
    
});

As u can see, in onCompleted we have access to the result of refetchQuery and we can use this to do whatever we want. Same with the onError function.
For more information about the accepted params you can check this section

@andefred
Copy link

andefred commented Oct 5, 2020

If anyone runs into this same issue, what fixed it for me since I've never had any issues with refetchQueries before, was this:

I had a loop over a child collection, and was then fetching the parent in a refetchQueries when a child was deleted. Setting the id or the child as key somehow made a difference... rule.id below

{data.rules_RuleSet &&
        data.rules_RuleSet.rules.map((rule, i) => <Rule rule={rule} key={rule.id} ruleSetId={ruleSetId} />)}

@jordiup
Copy link

jordiup commented Oct 21, 2020

The thing that fixed it for me was changing the fetchPolicy on the awaiting component. So that it didn't default to using the cache

const { data, error } = useGetProductsQuery({
fetchPolicy: 'cache-and-network'
});

@deepakbhattmits
Copy link

Hi @tokenvolt ,

I wanted to ask one question if we have fire any useMutation after that refetchQueries I want to display that data into the UI how to do that.

  const[signup,{loading,data,error}]=useMutation(SIGN_UP,{
    refetchQueries: [
      { query: CURRENT_USER }
    ]})

I want to print current User in the screen

@tufail
Copy link

tufail commented Jul 28, 2022

I am still having the issue, I am getting null value in data result but the same query is returning value in POSTMAN app. Is there anyone who faced the same issue , please help

const [addItem, { loading, data, error }] = useMutation(ADD_ITEM, {
    refetchQueries:  [{ query: GET_ITEMS }],
    awaitRefetchQueries: true,
  });

Response:
{"data":{"items":null}}

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests