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

Update store after mutation (create) unclear how to handle #1697

Closed
lucfranken opened this issue May 12, 2017 · 35 comments
Closed

Update store after mutation (create) unclear how to handle #1697

lucfranken opened this issue May 12, 2017 · 35 comments

Comments

@lucfranken
Copy link

There are a lot of docs but one part seems to be totally unclear on how to approach.

I use GraphCool as backend and React Native as frontend so that's how the names of the queries are setup.

Say I have a list of invoices

  • Invoice 1 Paid: true
  • Invoice 2 Paid: false

I have 2 queries (I show 2 lists on the screen): All & unpaid

query allInvoices {
    allInvoices {
      id
      paid
    }
}
query allInvoices {
    allInvoices(filter: {paid: false }) {
      id
      paid
    }
}

I insert a new invoice:

mutation createItemType(
    $name: String!,
    $paid: Boolean,
  ) {
    createInvoice(
      name: $name,
      paid: $paid,
    ) {
       id
       paid
    }
  }

I do understand that Apollo is not magic in a sense that it would automatically update my 2 views: all & unpaid. The advice in the documentation at this moment seems to be that I should use: update()

So I do that:

@graphql(gql`
  mutation createInvoice(
    $name: String!,
    $paid: Boolean,
  ) {
    createInvoice(
      name: $name,
      paid: $paid,
    ) {
      id,
      paid,
    }
  }
`, {
  name: 'createInvoice',
  options: {
    update: (proxy, { data: { createInvoice } }) => {
      const allInvoicesQuery = gql`
        query allInvoicesQuery {
          allInvoices {
            id
            paid
          }
        }
      `;

      const data = proxy.readQuery({ query: allInvoicesQuery });
      data.allInvoices.unshift(createInvoice);
      proxy.writeQuery({
        query: allInvoicesQuery,
        data
      });
    },
  },
})

The problem: The All invoices list will get updated. The one with the paid filter does not.

It's totally unclear how to get this right. I even tried adding variables to the writeQuery and readQuery but it just doesn't seem to work out. The simple examples in the tutorials DO work but it seems the more complex cases don't.

Questions:

  1. Where to find correct examples about real-life situations where you do filter data?
  2. Is all of this code really needed? It's a bug amount of code (I have many more fields) for just a create statement?
@saudpunjwani101
Copy link

in my case the update function doesn't gets called.

@lucfranken
Copy link
Author

Got some interesting feedback on Slack which I think should be added:
https://apollographql.slack.com/archives/C0Z9ZBDF0/p1495109398114586

As far as I understand for now I would have to do this part for EVERY query?

    const data = proxy.readQuery({ query: allInvoicesQuery });
      data.allInvoices.unshift(createInvoice);
      proxy.writeQuery({
        query: allInvoicesQuery,
        data
      });

So every time I do a query on the same nodes I would have to change all update calls? So all code becomes strongly dependent on each other? That makes all code hardcoded connected?

For example: In my "View account" component I cannot see my total amount of unpaid invoices unless I hardcode a writeQuery in my invoices list to update that component?

@jaydenseric
Copy link
Contributor

Not a "proper" solution, but I have given up on attempting smart cache busting after running mutations. I just decorate the component with withApollo and in the mutation success callback run this.props.client.resetStore(). Mutations don't happen that often, so it's not too bad that users have to refetch everything; in my app it adds less than a second.

@lucfranken
Copy link
Author

lucfranken commented May 19, 2017

While totally agreeing that this should not be needed it sounds like a very simple and effective fix for the time being!

Another alternative is just polling:

http://dev.apollodata.com/react/api-queries.html#graphql-config-options-pollInterval

export default graphql(gql`query { ... }`, {
  options: { pollInterval: 5000 },
})(MyComponent);

Even easier because you can implement it without any callback.

@jurajkrivda
Copy link

You can get all the invoices and then filter those that are paid. Then you can use the mutation recommended above with readQuery and writeQuery helpers.

@lucfranken
Copy link
Author

Thanks for the feedback @jurajkrivda !

I think if I understand you well:

  const data = proxy.readQuery({ query: allInvoicesQuery });

  data.allInvoices.push(createInvoice);
  proxy.writeQuery({
    query: allInvoicesQuery,
    data
  });

  // Then manually filter that array by paid = true and write it to 
  // allInvoices(filter: {paid: true })

  // Then manually filter that array by paid = false and write it to 
  // allInvoices(filter: {paid: false })

That's the logic? While I would hope that it seems duplicated logic (I already wrote that query) and very cumbersome if you get additional parameters as it will become a matrix?

Let's say I have 3 boolean parameters that's already 2 * 2 * 2 = 8 manual filters I have to implement to update the data? If I have more it becomes a huge amount?

@stale
Copy link

stale bot commented Jul 15, 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!

@lucfranken
Copy link
Author

@probot-stale[bot] actually this issue is still open and wondering whether there are any new strategies which have been developed recently.

@ctavan
Copy link

ctavan commented Jul 17, 2017

@lucfranken first of all I believe that this is a duplicate of (or at least related with) apollographql/react-apollo#708

In fact I ran into exactly the same issue and ended up calling all queries in the update hook with all parameters as you describe. This is particularly bad since you don't really know which of all those queries are already in the store and actually need an update. So after an update you end up with a store that is much large than what would often be necessary.

I was hoping that I could at least simplify my case by using refetchQueries which would be a little less radical than resetting the entire store as suggested by @jaydenseric. When using refetchQueries with just the query name it should actually refresh all queries with a given name in the store, irrespective of the variables/parametrizations. Unfortunately it currently doesn't work as expected due to a race-condition which I have reported in: #1821

So for now I'm stuck with the repeated store updates which works but really adds a lot of logic and code while I would find a simple refetch of all queries that are already in the store to be a good compromise between maintainability and client-side performance.

Edit: Oh, and as you mention in your last comment @lucfranken, it also only works for me because I currently have queries with one boolean parameter. As you mention this would soon become impossible for more complex parametrizations (think: pagination where you don't actually know the total number of pages)…

@lucfranken
Copy link
Author

@ctavan yes party it's related but this issue actually was created based on the recommendations in the documentation that the refetchQueries would be deprecated. Input from the Apollo team on the right direction is needed.

@stale
Copy link

stale bot commented Aug 14, 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!

@pawsong
Copy link

pawsong commented Aug 19, 2017

It would be helpful if there is an API like readQueries or readQueryWithVariables which returns all cached parameters of a parameterized query. Then I can manually update each query caches. My 2 cents :)

@stale
Copy link

stale bot commented Sep 9, 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
Copy link

stale bot commented Sep 23, 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!

@stale stale bot closed this as completed Sep 23, 2017
@aunnnn
Copy link

aunnnn commented Oct 5, 2017

Hi, is there any updates on this issue?

So now I have to either:

  1. use 'refetchQueries' or,
  2. use 'update' to update the store for all related queries,

Is that right?

@lucfranken
Copy link
Author

@aunnnn this issue got closed automatically. Asked about that here: #1910

As I wrote months ago:

Input from the Apollo team on the right direction is needed.

No idea why there is no answer.

@adavia
Copy link

adavia commented Jan 3, 2018

I had experience a situation like this where i needed to update an item from a filtered list and i handled it using the componentWillReceiveProps lifecycle method.

Example

componentWillReceiveProps(newProps) {
    const { allInvoices, loading } = newProps.data;

    if (!loading) {
      const invoices = _.intersectionBy(allInvoices, this.state.invoices, 'id');
      this.setState({ invoices });
    }
  }

@Vitiell0
Copy link

If anyone is still running into this problem, we have found a solution. Just pass the variables with submit and they will be available in update.

screen shot 2018-01-25 at 12 20 09 pm

@minardimedia
Copy link

Why the Apollo team is ignoring this. It is a to common problem and they are no doing anything about it. Their may be maybe a way but there is nothing document about it really dissapointing

@lucfranken
Copy link
Author

It seems that refetchQueries is not deprecated anymore. All docs are shuffled around so it's not easy to find back the early mentions of this but it seems changed in the docs.

@mech-tools
Copy link

mech-tools commented Apr 20, 2018

How am I supposed to know which argument I should use with refetchQueries ? refetchQueries doesn't seem to be a solution either ...

@renato
Copy link

renato commented Jun 4, 2018

@DarKDinDoN I'd prefer a better solution using update, but refetchQueries also works with strings, like this:

<Mutation mutation={CREATE_FOO} refetchQueries={["allFoos"]}>

From https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-mutation-options-refetchQueries:

If options.refetchQueries is an array of strings then Apollo Client will look for any queries with the same names as the provided strings and will refetch those queries with their current variables.

@mech-tools
Copy link

@renato yes but am I supposed to know which argument I should provide to the query ? Especially, when the parameters are dynamic.

@renato
Copy link

renato commented Jun 4, 2018

No, using strings those queries will be refetched using their current variables - which may solve some situations.

I'll probably use it whenever possible or do some kind of cache invalidation (ideas on #621 #899) otherwise.

@minardimedia
Copy link

@DarKDinDoN I try it all I get your point their is no way you can know all the multiple dynamic queries that are being generated by the users. I still don't know why if is such a common thing they don't provide a way to refetch ALL.

Anyway if works for something my solution was to Flush the whole cache, I know it's not a nice solution but works for me since I don't expect user will be adding and removing to much elements anyway. Also cache refresh more often that we think like for example if users recharge the page so it is no that big o a deal.

Here is how can you implement this.

`
import { withApollo } from 'react-apollo';

export default withApollo(YOURCOMPONENT);

//this will give you access to the client as a prop so inside your component, after the add or delete action you just call

this.props.client.resetStore();
`

@acomito
Copy link

acomito commented Jun 17, 2018

@minardimedia one issue I have with resetStore is it includes reseting my auth/user queries, which causes mayhem in apps that have an "authenticated" area (where user would get kicked back to login or content would flicker).

@minardimedia
Copy link

Hi @acomito I just came to know about this repo apparently it works for this issue, I haven't try it but you can take a look at it (apollo-link-watched-mutation) https://github.com/haytko/apollo-link-watched-mutation

@yashwp
Copy link

yashwp commented Jul 10, 2018

I'm working with this in apollo-angular -
screenshot 14
I hope that helped :)

@mech-tools
Copy link

@yashwp thanks, your example covers a very simple example. Try it when ALL_COURSES has many filters/parameters.
Y'll that it is not scalable to cover every QUERY and their parameters by hand.

@vitaliy-ostapchuk93
Copy link

@yashwp Thanks! Tried it out on my own models and it works! very simple and nice code :)

@expelledboy
Copy link

Why is this still marked as closed? Clearly there hasnt been a satisfactory resolution to this issue. Can we disable the bot for this issue?

@MaxmaxmaximusGitHub
Copy link

sooooo?

@mikeRChambers610
Copy link

?????

@awlevin
Copy link

awlevin commented Dec 3, 2020

Chiming in, but without a solution. Agree with the rest of you that mutation insertions on cached + paginated + sorted queries feels like a pretty common use-case, and it would be wonderful to see a more formal solution for this in Apollo.

@mindnektar
Copy link

mindnektar commented Jun 3, 2021

I've created a package in an attempt to solve these issues and make complex cache updates as painless as possible. https://www.npmjs.com/package/apollo-augmented-hooks

See https://github.com/appmotion/apollo-augmented-hooks/#--modifiers and the linked caching guide in particular.

I've been using this in production for a while now and just made it open source so other people can benefit from it as well.

@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