6

enter image description here

I have both the getMovies query and addMovie mutation working. When addMovie happens though, I'm wondering how to best update the list of movies in "Edit Movies" and "My Profile" to reflect the changes. I just need a general/high-level overview, or even just the name of a concept if it's simple, on how to make this happen.

My initial thought was just to hold all of the movies in my Redux store. When the mutation finishes, it should return the newly added movie, which I can concatenate to the movies of my store.

After "Add Movie", it would pop back to the "Edit Movies" screen where you should be able to see the newly added movie, then if you go back to "My Profile", it'd be there too.

Is there a better way to do this than holding it all in my own Redux store? Is there any Apollo magic I don't know about that could possibly handle this update for me?


EDIT: I discovered the idea of updateQueries: http://dev.apollodata.com/react/cache-updates.html#updateQueries I think this is what I want (please let me know if this is not the right approach). This seems better than the traditional way of using my own Redux store.

// this represents the 3rd screen in my picture
const AddMovieWithData = compose(
  graphql(searchMovies, {
    props: ({ mutate }) => ({
      search: (query) => mutate({ variables: { query } }),
    }),
  }),
  graphql(addMovie, {
    props: ({ mutate }) => ({
      addMovie: (user_id, movieId) => mutate({
        variables: { user_id, movieId },
        updateQueries: {
          getMovies: (prev, { mutationResult }) => {
            // my mutation returns just the newly added movie
            const newMovie = mutationResult.data.addMovie;

            return update(prev, {
              getMovies: {
                $unshift: [newMovie],
              },
            });
          },
        },
      }),
    }),
  })
)(AddMovie);

After addMovie mutation, this properly updates the view in "My Profile" because it uses the getMovies query (woah)! I'm then passing these movies as props into "Edit Movies", so how do I update it there as well? Should I just have them both use the getMovies query? Is there a way to pull the new result of getMovies out of the store, so I can reuse it on "Edit Movies" without doing the query again?


EDIT2: Wrapping MyProfile and EditMovies both with getMovies query container seems to work fine. After addMovie, it's updated in both places due to updateQueries on getMovies. It's fast too. I think it's being cached?

It all works, so I guess this just becomes a question of: Was this the best approach?

GreenAsJade
  • 14,002
  • 9
  • 54
  • 91
atkayla
  • 6,193
  • 11
  • 53
  • 99
  • for Apollo magic, show some code.. – Fazal Rasel Oct 19 '16 at 18:30
  • 1
    When you say "Was this the best approach?" you risk getting your question closed due to it being opinion based. However, I came here to write the answer that you came up with based on the question in the title (a well written title BTW!) so I'll do that ;) – GreenAsJade Oct 19 '16 at 22:39

2 Answers2

1

The answer to the question in the title is

Use updateQueries to "inform` the queries that drive the other views that the data has changed (as you discovered).

This topic gets ongoing discussion in the react-apollo slack channel, and this answer is the consensus that I'm aware of: there's no obvious alternative.

Note that you can update more than one query (that's why the name is plural, and the argument is an object containing keys that match the name of all the queries that need updating).

As you may guess, this "pattern" does mean that you need to be careful in designing and using queries to make life easy and maintainable in designing mutations. More common queires means less chance that you miss one in a mutation updateQueries action.

GreenAsJade
  • 14,002
  • 9
  • 54
  • 91
  • Thanks! Yeah, when I discovered `updateQueries`, I was pretty amazed at how well it just worked, but being a newbie, I can't help but feel a little uncomfortable when things actually work, especially with bleeding edge tech! Do you have any thoughts for my side-question? Originally, only "My Profile" had the `getMovies` query, and I was just passing movies as props into "Edit Movies". Of course, this means that after the mutation, it only updates where `getMovies` is... so only on "My Profile". Was adding `getMovies` to "Edit Movies", so it updates there too, the right thing to do here? – atkayla Oct 19 '16 at 22:47
  • This is what I allude to in the "careful design and use of queries" :) I think the answer is "yes". Note that other options include having both those components inside another one that is the container, and does the query, and passes the move list as props. This is only an option if it makes sense in the UI design though (in my app that looks just like yours, "edit movies" is a modal and so can take props from "my profile" and gets updated when "my profile" container does... but modals are often not a great idea :O ). Maybe see you in https://apollostack.slack.com/archives/react-apollo – GreenAsJade Oct 19 '16 at 22:53
  • From the official docs: "We recommend using `update` instead of `updateQueries`. updateQueries will be removed in the next version of Apollo Client." [src](https://www.apollographql.com/docs/react/api/react-apollo#graphql-mutation-options-updateQueries) – maninak May 13 '19 at 14:17
1

The Apollo Client only updates the store on update mutations. So when you use create or delete mutations you need to tell Apollo Client how to update. I had expected the store to update automatically but it doesn’t…

I have founded a workaround with resetStore just after doing your mutation. You reset the store just after doing the mutation. Then when you will need to query, the store is empty, so apollo refetch fresh data.

here is the code:

import { withApollo } from 'react-apollo'

...

  deleteCar = async id => {
    await this.props.deleteCar({
      variables: { where: {
        id: id
      } },
    })
    this.props.client.resetStore().then(data=> {
      this.props.history.push('/cars')
    })
  }


...


export default compose(
  graphql(POST_QUERY, {
    name: 'carQuery',
    options: props => ({
      fetchPolicy: 'network-only',
      variables: {
        where: {
          id: props.match.params.id,
        }
      },
    }),
  }),
  graphql(DELETE_MUTATION, {
    name: 'deleteCar',
  }),
  withRouter,
  withApollo
)(DetailPage)

The full code is here: https://github.com/alan345/naperg Ther error before the hack resetStore enter image description here

Alan
  • 4,576
  • 25
  • 43