apollographql / apollo-client

:rocket:  A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server.
https://apollographql.com/client
MIT License
19.37k stars 2.66k forks source link

refetchQueries not working when using string array after mutation #5419

Open reinvanimschoot opened 5 years ago

reinvanimschoot commented 5 years ago

Intended outcome: After a create mutation, I want to refetch the list query to have the newly added item be displayed in the list, making use of the variables which the list query is run with previously.

As per the documentation, this should be doable:

Please note that if you call refetchQueries with an array of strings, then Apollo Client will look for any previously called queries that have the same names as the provided strings. It will then refetch those queries with their current variables.

Actual outcome: When using the operation name as string in the refetchQueries array, no query is refetched, nothing is updated and no network activity is visible.

Versions

  System:
    OS: macOS Mojave 10.14.6
  Binaries:
    Node: 10.16.3 - ~/.nvm/versions/node/v10.16.3/bin/node
    Yarn: 1.19.0 - /usr/local/bin/yarn
    npm: 6.9.0 - ~/.nvm/versions/node/v10.16.3/bin/npm
  Browsers:
    Chrome: 77.0.3865.90
    Safari: 13.0.1
  npmPackages:
    @apollo/react-hooks: ^3.1.0 => 3.1.1
    apollo-cache-inmemory: ^1.6.3 => 1.6.3
    apollo-cache-persist: ^0.1.1 => 0.1.1
    apollo-client: ^2.6.4 => 2.6.4
    apollo-link: ^1.2.13 => 1.2.13
    apollo-link-context: ^1.0.19 => 1.0.19
    apollo-link-error: ^1.1.12 => 1.1.12
    apollo-link-http: ^1.5.16 => 1.5.16
    apollo-link-logger: ^1.2.3 => 1.2.3
    react-apollo: ^3.1.2 => 3.1.2
jinshin1013 commented 5 years ago

I had a similar problem but was resolved by providing the exact object used to fetch the query which may include any variables, query options and etc.

reinvanimschoot commented 5 years ago

The whole purpose of using a string is that you should NOT have to provide the exact object as Apollo should automatically refetch the query with the current query variables.

jinshin1013 commented 5 years ago

Yeah I totally agree with you there man it sure is a bug or documentation is wrong. Didn't mean to sound like it ain't a bug. It's very inconvenient having to provide the exact same object especially multiple mutations in multiple places refetches the query.

tafelito commented 5 years ago

I have a similar issue. The weird thing is that when I call a mutation where it deletes a row, and then I call the refetchQueries with a string, it actually works. But after I insert a new row, nothing happens when I do the refetch. It makes no sense because the delete is still a mutation, so I'm not sure what the difference could be. Both mutations return the same values. I'm looking at the network requests, and I see that after the insert there is no request, but after the delete, there is a new request that actually do re fetch

The only way I could make this to work is to change the fetch policy to cache-and-network

delete mutation image

refetch image

insert mutation image

delete mutation hook image

insert mutation hook image

reinvanimschoot commented 5 years ago

I have witnessed exactly the same behaviour! It works fine when I refetch after I delete an item but not when I create one.

mnesarco commented 4 years ago

Hi Friends, I have the same problem: refetchQueries works with update mutations, but nothing happens on insert mutations. Is there any known workaround?

kirkchris commented 4 years ago

Impacting us here also... 😫

seanconrad1 commented 4 years ago

Also running into this issue

onderonur commented 4 years ago

I came accros this issue. I create a new item from a modal, while the listing page is in the background. After the mutation, I redirect to the detail page of the new item. Also, when the mutation is completed, I can see the listing query is refetched (in browser dev-tools). But the listing query result in the cache not getting updated.

If I cancel the redirect, which comes right after the mutation, there is no problem. After the query is refetched, cache gets updated. If I keep the redirect, but make awaitRefetchQueries: true in the mutation options, there is no problem again.

I think, when the page that observes the query gets unmounted, even if the network request gets completed, cache can not be updated.

A cool way could be invalidating a query even if it wasn't observed by any active component. Using this way, one could just tell the cache that the query result is now invalidated. And the first call to that query would be done by a network request. So, user creates a new record. Get redirected to the detail page of the new item. User may go to other pages, it doesn't matter. But when we come to the listing page again, we would trigger a network request, instead of showing the stale data in the cache.

This is just a simple idea of course. There may be positive/negative points about this.

Edit: After some thinking, I feel like using refetchQueries to refresh active queries and fetchPolicy: network-only for things like redirecting to listing pages might be much much more easy to implement and more maintainable. Using cache results is a very strong technique for things like infinite loaders, multiple isolated components which use the response from same query etc.

Most of the times I just don't feel it's ok to change the default fetchPolicy, but it is a good option to use for many use cases.

cristiandley commented 4 years ago

6017

3nvi commented 4 years ago

I have witnessed exactly the same behaviour! It works fine when I refetch after I delete an item but not when I create one.

In case anyone is still interested, this behavior happens because when you delete something, the component holding the original query didn't get unmounted (normally because you just show a popup), while when creating a new item you redirect to another screen (i.e. create item page) unmounting the page holding the original query.

Apollo maintains a list of observable queries at any given time, which is based on which components are currently mounted that defined a query (i.e. through <Query>...</Query> or useQuery). When a component gets unmounted - at least in the React world - , Apollo removes it from the list of observable queries by calling QueryManager.tearDownQuery when the component unmounts. During refetchQueries , when given a Query name string, apollo searches only the observable queries and because it can't find it, it doesn't execute a refeetch. If you provide a { query: ..., variables: .. } as a refetch value, Apollo bypasses this search and always executes the query you've given it, as if it was a completely new query (since it has the doc and the variables it needs to execute it)

I don't know if @hwillson would like to give us more context as to whether there is a way around this (except from not using the React or by making sure that we unmount as little as possible on crucial refetches).

In short:

refetchQueries will only work with strings if the component that defined the original query is not unmounted. On the contrary, it will always work when using the { query... , variables: ... } style.

Emiliano-Bucci commented 4 years ago

So it's not possible to perform a refetch with the original query and variables? Doesn't sound to me a good choice :/

3nvi commented 4 years ago

So it's not possible to perform a refetch with the original query and variables? Doesn't sound to me a good choice :/

It's only possible if you don't navigate away (i.e. unmount the component that performed the original query).

Your other option (which is not actually a solution) is to use a different networkPolicy so that when you revisit the component that held the query, a server-query is performed.

Emiliano-Bucci commented 4 years ago

I see; thanks!

Momepukku commented 4 years ago

@3nvi

refetchQueries will only work with strings if the component that defined the original query is not unmounted.

I have three components render on the same page which use the same query(and same operation name) but difference variables. when I specify operationName as refetchQueries, only the last one got refetch. Why?

3nvi commented 4 years ago

cause it literally does just that. It refetches the query with the latest variables. From apollo's side it's only 1 query with 3 different invocations, so i tries to refetch the "latest" invocation.

slask commented 4 years ago

Still an issue in 3.1.5 on May 22nd 2020. This is an important feature, frequently used, that should be fixed

moneebalalfi commented 4 years ago

What happened with this issue, please!!

Idan-Hen commented 4 years ago

I think this needs to have option to refetch non observable queries. and also not latest varibales - but all variables for this query

those flags can solve a lot of issues

jdmoliner commented 3 years ago

Apollo client 3.2.5 and issue is not fixed. Any updates?

andoks commented 3 years ago

May be related to #3540

espinhogr commented 3 years ago

I have a very similar problem to the one you are experiencing here, I'm on version 3.3.11. I am basically using refetchQueries with a string array from a component A and I expect the component B to have the query refreshed when it's mounted again. But I've noticed something that hasn't been mentioned here. If I run the code in development mode the code behaves as expected, if I do run in production mode (after the yarn build) I experience the same problem of refetchQueries not working. Not sure any of you experiences this as well.

thomasjsk commented 3 years ago

@espinhogr got the same issue. In my case refetch was performed on a component that was already unmounted, and that - for some reason worked well on dev, but didn't on prod. Check your warnings (red ones lol), maybe that's the case for you as well.

mutefiRe commented 3 years ago

refetchQueries will only work with strings if the component that defined the original query is not unmounted. On the contrary, it will always work when using the { query..., variables: ... } style.

on the other side, refetchQueries with the { query..., variables: ... } style also refetches queries that never were queried before. Which is also a behavior I would not prefer in our use cases.

manuFL commented 3 years ago

Same here, working on dev but not on prod.... please help

maxsalven commented 3 years ago

This is still an issue on 3.4.7

Reproduction: https://codesandbox.io/s/floral-sea-cg45v?file=/src/App.js

Navigate to 'Mutation' and click 'Mutate'.

You'll see a console warning:

Unknown query named "Slim" requested in refetchQueries options.include array

However as per the docs https://www.apollographql.com/docs/react/data/mutations/#refetching-queries

The name of a query you've previously executed, as a string

it should work, as the query Slim was previously executed. The issue seems to be that the component with the query is no longer in the tree, but the docs don't mention that as a requirement. I'm not sure if the docs are wrong, or it's a bug.

kalote commented 3 years ago

I faced this issue today.

Same situation (mutation from another component, not in the tree anymore, that need to refetch a query upon success).

Is there any workaround?

Off2Race commented 3 years ago

Hi, @kalote – I ran into this issue as well. The only workaround I could come up with was to use the new client.refetchQueries method, specify all "active" queries, and filter out the ones I didn't watch to refetch using the onQueryUpdated function. See the example below:

client.refetchQueries({
    include: 'active',
    onQueryUpdated(observableQuery) {
        switch (observableQuery.queryName) {
            case 'QueryIWantToRefresh':
                return true;
        }

        return false;
    },
});
sepehrg commented 3 years ago

I faced the same problem. Any updates?

benjamn commented 3 years ago

@Off2Race I would expect that code to be equivalent to the following:

client.refetchQueries({
  include: ["QueryIWantToRefresh"],
})

Does that not work for you? Or was your point just that you had to use client.refetchQueries instead of the refetchQueries option for mutations?

Off2Race commented 3 years ago

Hi, @benjamn – I actually haven't tried the simpler form you suggested but I think it might have the same problem.

The real key to my workaround was making sure QueryIWantToRefresh is active before it's refreshed. Using client.refetchQueries instead of the refetchQueries option was really just the means to do that.

The onQueryUpdated option by itself is a great new tool for development. In this context, though, it was the only way I could figure out how to pre-validate that a query was active/mounted before asking that it be refreshed.

All that said, I can try the version you suggested. Would you expect it to work (i.e. not throw the error)?

pbassut commented 3 years ago

I can confirm @Off2Race solution works(the long version but not the short one from @benjamn. Despite them looking the same). But that's is uhhhh-gly

mustafamoe commented 3 years ago

still, an issue for me too, am deleting some entities in one page and it's updating the cache as you would expect, but when I create an entity on another page it's doesn't update the cache when I come back to the entity listing page.

benjamn commented 3 years ago

Based on this discussion, I have a theory I'd like to test (see #8825), which is that QueryIWantToRefresh is not refetched because it happens to have no observers, even though the developer explicitly requested the refetch.

Off2Race commented 3 years ago

Hi, @benjamn – Once you have a new version with #8825, I'm happy to test with my use case. I'm on 3.4.8 now, so it should be an easy upgrade. Just let me know how I can help.

Off2Race commented 3 years ago

Hi, @benjamn – I just upgraded my project to 3.4.16 (which should have #8825) but I'm still getting the following warning when I use refetchQueries with mutations.

Unknown query named "QueryIWantToRefresh" requested in refetchQueries options.include array

In this case, QueryIWantToRefresh is used in various other components that weren't mounted at the time the mutation was executed. The warning appears benign (more a nuisance than anything). And fortunately, the workaround I posted earlier still seems to work. But I thought you'd want to know.

Is there other information I can share to help you figure out the cause? Should I log a new GitHub issue since this one is closed?

Thanks!

szamanr commented 3 years ago

having the same issue on 3.4.16. the query from an unmounted component is not refetched, regardless of whether i specify it using refetchQueries when defining a mutation or call apolloClient.refetchQueries.

Off2Race commented 3 years ago

Hi, @szamanr – To be fair, I'm not sure it matters whether the queries are actually refetched because the components themselves are unmounted. When they do mount, the queries should update normally. In our case, it's the warning itself which causes concern. It's either just a nuisance (causing alarm where none should exist) or it points to a separate problem that we can't see.

emilioschepis commented 3 years ago

Same issue as the ones described above.

I understand that the queries I'm trying to refetch might not be available. In my case, that depends on the way the user got to a given page.

However, I don't think we should get a warning for this. If the original behavior must be preserved, maybe add a refetchQueriesIfAvailable which behaves identically but without the warning?

daveslutzkin commented 2 years ago

We have the identical issue to @szamanr.

@Off2Race - unfortunately it's not as simple as "When they do mount, the queries should update normally." Of course that does happen, but in this case "updating normally" is pulling the data from the cache, which is now out of date because of the mutation where we asked for refetch.

Partly this is a fundamental issue with the Apollo design, where instead of manual cache invalidation the choice was query refetching, which is a slightly different thing and harder to achieve (and provably more expensive). If you look at react-query (https://react-query.tanstack.com/guides/query-invalidation) it does it the other way and turns out to be much simpler and easier to use in practice.

@benjamn I had hoped that https://github.com/apollographql/apollo-client/pull/8825 would fix this but unfortunately I'm still seeing it in 3.4.16 post-merge of that PR.

Apollo needs to either:

The last one here would treat it as an invalidation request, which presumably 99% of the time is the desired behaviour. Most apps don't really care if the refetch happens immediately or next time the data is required (if at all).

daveslutzkin commented 2 years ago

What I'm doing to make this work is to just manually evicting the data. Feels a bit more low-level than is ideal but I guess that's by design?

szamanr commented 2 years ago

@daveslutzkin i guess a workaround is to set the cache policy to "cache-and-network" on the request you're trying to refetch.

giggo1604 commented 2 years ago

Looks like there is something wrong with the defaultOptions. As stated in the docs the default fetch policy for a watchQuery, which is used by the useQuery hook, should be cache-and-network. If I manually set the defaultOptions on client creation everything works as expected.

nilesh-05 commented 2 years ago

I am having the same issue. After a book gets inserted in my database, I need to get all books again to show them in the list. But refetchQueries is not working.

const [addBook, response] = useMutation(CREATE_BOOK, {
    variables: { name, genre, authorID },
    refetchQueries: [{ GET_BOOKS }],
    awaitRefetchQueries: true,
});

Each time after adding a book, I have to manually refresh my page. Version: "@apollo/client": "^3.4.16", "graphql": "^15.7.2",

Luchanso commented 2 years ago

I have simple workaround solution for this - just add that query in the same function with mutation:

function ProductAdd() {
  const result = useQuery(GET_SHOP, { ... });
  const [...] = useMutation(
    CREATE_PRODUCT,
    {
      refetchQueries: ["GetShop"],
    }
  );
  // ...
}

@benjamn maybe reopen issue, what you think?

marcelchastain commented 2 years ago

I was able to fix this issue in my local project, though the solution might not apply to everyone.

I'm using getDataFromTree() , then

const initialState = apolloClient.extract();

That's being passed to my page in the normal SSR fashion, via pageContext (or more commonly, window.__APOLLO_STATE__ or something)

I found that in my app's page render/hydration function I needed to get ahold of that value and call

    // Updated state is being passed to us by the server on every page load,
    // which got it from getDataFromTree(), so we need to restore on every
    // page transition.
    apolloClient.restore(apolloInitialState);

My theory is that, in my particular setup, the getDataFromTree() caused the queries to be "helpfully" executed server-side, which is why I wasn't seeing the additional refetch network request in my browser as I was expecting.

If you're using anything similar, I'd say inspect your pageContext (or window.__APOLLO_STATE__) to see if the refetched data is indeed reflected there.

(Note this only became in specific scenarios, e.g. when I deleted an object and redirected afterwards.)

Vuliniar commented 2 years ago

It would be great if we could have support for refetching unmounted/unobservable queries.

ddkang commented 2 years ago

Why was this issue closed? It looks like there's a lot of errors even in recent releases? I'm also running into similar issues

performautodev commented 2 years ago

same

jpvajda commented 2 years ago

Thanks everyone for the comments and feedback we appreciate it! The Apollo Client team is taking a look at this problem again and will keep you all informed of when a fix will be released.