{"action":"opened","issue":{"url":"https://api.github.com/repos/apollostack/react-apollo/issues/232","repository_url":"https://api.github.com/repos/apollostack/react-apollo","labels_url":"https://api.github.com/repos/apollostack/react-apollo/issues/232/labels{/name}","comments_url":"https://api.github.com/repos/apollostack/react-apollo/issues/232/comments","events_url":"https://api.github.com/repos/apollostack/react-apollo/issues/232/events","html_url":"https://github.com/apollostack/react-apollo/issues/232","id":179380690,"number":232,"title":"updateQueries gets extremely contrived when trying to update lists","user":{"login":"migueloller","id":5677929,"avatar_url":"https://avatars.githubusercontent.com/u/5677929?v=3","gravatar_id":"","url":"https://api.github.com/users/migueloller","html_url":"https://github.com/migueloller","followers_url":"https://api.github.com/users/migueloller/followers","following_url":"https://api.github.com/users/migueloller/following{/other_user}","gists_url":"https://api.github.com/users/migueloller/gists{/gist_id}","starred_url":"https://api.github.com/users/migueloller/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/migueloller/subscriptions","organizations_url":"https://api.github.com/users/migueloller/orgs","repos_url":"https://api.github.com/users/migueloller/repos","events_url":"https://api.github.com/users/migueloller/events{/privacy}","received_events_url":"https://api.github.com/users/migueloller/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2016-09-27T02:06:52Z","updated_at":"2016-09-27T02:06:52Z","closed_at":null,"body":"I'm using `updateQueries` to alter a list when a user \"likes\" or \"dislikes\" one of the list items but I've run into many issues which I will try to explain below.\r\n\r\nMy mutation query looked something like this:\r\n```graphql\r\nmutation ($itemId: ID!) {\r\n  item: toggleLike(itemId: $itemId) {\r\n    id # needed for cache\r\n    liked: viewerHasLiked\r\n  }\r\n}\r\n```\r\nThe first issue I came across is that if I wanted to update the list, I needed to query a lot more than simply `id` and `viewerHasLiked` because the list query expects many more fields. After adding these fields I had another issue, I was using optimistic updates so now I had to change that as well in order to not get Apollo errors. At this point it gets very complicated because it's impossible to predict the optimistic result of the entire item and putting dummy data would break rendering.\r\n\r\nSo here's how I tried to solve it, I simply didn't try to update the query when the result was optimistic (by checking for fields present). The issue with that is that now that the query had all these new fields, the optimistic response HAD to include them.\r\n\r\nUltimately I had to patch up a very hacky solution. I used `withApollo` on the component so that I could access the client via `ownProps`. With that, my higher order component ended up looking like this:\r\n```js\r\nexport default graphql(gql`\r\n  mutation ($itemId: ID!) {\r\n    item: toggleLike(itemId: $itemId) {\r\n      id # needed for cache\r\n      liked: viewerHasLiked\r\n    }\r\n  }\r\n`, {\r\n  options: ({ itemId }) => ({ variables: { itemId } }),\r\n  props: ({ ownProps: { liked, itemId, client }, mutate }) => ({\r\n    toggleLike: () => mutate({\r\n      optimisticResponse: {\r\n        item: {\r\n          id: itemId,\r\n          liked: !liked,\r\n          __typename: 'Item',\r\n        },\r\n      },\r\n      updateQueries: {\r\n        List: (prev, { mutationResult: { data: { item } } }) => {\r\n          const { queryManager } = client;\r\n          const [queryId] = queryManager.queryIdsByName.SomeQueryNameThatHasSufficientData;\r\n          const itemLiked = queryManager.queryResults[queryId].data.items.edges\r\n            .map(({ node }) => node).find(({ id }) => id === item.id);\r\n          return update(prev, {\r\n            viewer: {\r\n              list: {\r\n                edges: item.liked\r\n                  ? { $push: [{ __typename: 'ItemEdge', item: itemLiked }] }\r\n                  : { $apply: (edges) => edges.filter(({ item: { id } }) => id !== item.id) },\r\n              },\r\n            },\r\n          });\r\n        },\r\n      },\r\n    }),\r\n  }),\r\n});\r\n```\r\nAs you can see, this is suboptimal.\r\n\r\nInitially I thought the best way to solve this would be by passing the store as a last argument to the reducer map for `updateQueries` but then I realized that because this data is normalized, it's not useful. Given this, I had to go into the client's `queryManager` which is extremely undesirable.\r\n\r\nHow can we better prevent having to do something like this?\r\n\r\nI could've changed the architecture of the app so that the list was only an array of ids and then each rendered item got it's details but that would've increased the number of requests to the server (I could've done query batching but I've been getting various errors with it so it wasn't an option).\r\n\r\nThe cleanest solution that comes to mind would be to expose all of the queries as the last argument to the reducers in `updateQueries` so that the user doesn't have to pass in the client and go into the `queryManager`. Perhaps, the query manager itself could be passed? (but then how do you access the query by name?\r\n\r\nWould love to hear your thoughts on this @tmeasday, @helfer, and @stubailo.\r\n\r\nThanks!"}}
{"actor":{"display_login":"migueloller"}}