Repository: apollographql/apollo-client

Problem statement:
Variables in reducer for query always use the original variables
**Intended outcome:**
After setVariables, the reducers should re-run with the new variables for the query.

**Actual outcome:**
reducers always run with original variables.

**How to reproduce the issue:**
```
query = apollo.watchQuery(query: q, variables: { test: "A" }, 
  reducer: (prev, action, variables) => { 
     if (action.type == 'APOLLO_MUTATION_RESULT') {
        console.log(variables.test); 
     }
     return prev 
  }
).subscribe();

apollo.mutate(...)
// console => A
query.setVariables({ test: "B" })
apollo.mutate(...)
// console => A
// >>>> EXPECTED: B
```

<!-- 
**Intended outcome:**
What you were trying to accomplish when the bug occurred, and as much code as possible related to the source of the problem.

**Actual outcome:**
A description of what actually happened, including a screenshot or copy-paste of any related error messages, logs, or other output that might be related. Places to look for information include your browser console, server console, and network logs. Please avoid non-specific phrases like “didn’t work” or “broke”.

**How to reproduce the issue:**
Instructions for how the issue can be reproduced by a maintainer or contributor. Be as specific as possible, and only mention what is necessary to reproduce the bug. If possible, try to isolate the exact circumstances in which the bug occurs and avoid speculation over what the cause might be.
-->



Hints:

Patch:
diff --git a/src/core/QueryManager.ts b/src/core/QueryManager.ts
--- a/src/core/QueryManager.ts
+++ b/src/core/QueryManager.ts
@@ -877,14 +877,16 @@ export class QueryManager {
 
   private getExtraReducers(): ApolloReducer[] {
     return  Object.keys(this.observableQueries).map( obsQueryId => {
-      const queryOptions = this.observableQueries[obsQueryId].observableQuery.options;
+      const query = this.observableQueries[obsQueryId].observableQuery;
+      const queryOptions = query.options;
+
       if (queryOptions.reducer) {
         return createStoreReducer(
           queryOptions.reducer,
           queryOptions.query,
-          queryOptions.variables || {},
+          query.variables || {},
           this.reducerConfig,
-          );
+        );
       }
       return null as never;
     }).filter( reducer => reducer !== null );


Test patch:
diff --git a/test/ObservableQuery.ts b/test/ObservableQuery.ts
--- a/test/ObservableQuery.ts
+++ b/test/ObservableQuery.ts
@@ -782,6 +782,66 @@ describe('ObservableQuery', () => {
           }
         });
       });
+
+      it('applies query reducers with correct variables', (done) => {
+        const queryManager = mockQueryManager({
+          // First we make the query
+          request: { query, variables },
+          result: { data: dataOne },
+        }, {
+          // Then we make a mutation
+          request: { query: mutation },
+          result: { data: mutationData },
+        }, {
+          // Then we make another query
+          request: { query, variables: differentVariables },
+          result: { data: dataTwo },
+        }, {
+          // Then we make another mutation
+          request: { query: mutation },
+          result: { data: mutationData },
+        });
+
+
+        let lastReducerVars: Array<Object> = [];
+        let lastReducerData: Array<Object> = [];
+        const observable = queryManager.watchQuery({
+          query,
+          variables,
+          reducer: (previous, action, reducerVars) => {
+            if (action.type === 'APOLLO_MUTATION_RESULT') {
+              // We want to track the history of the `variables` the reducer
+              // is given for the query.
+              lastReducerData.push(previous);
+              lastReducerVars.push(reducerVars);
+            }
+
+            return previous;
+          },
+        });
+
+        // Check that the variables fed into the reducer are correct.
+        function assertVariables() {
+          assert.lengthOf(lastReducerVars, 2);
+          assert.deepEqual(lastReducerVars[0], variables);
+          assert.deepEqual(lastReducerData[0], dataOne);
+          assert.deepEqual(lastReducerVars[1], differentVariables);
+          assert.deepEqual(lastReducerData[1], dataTwo);
+          done();
+        }
+
+        // Subscribe to the query, then run the mutation, then change the variables, then run another mutation.
+        let sub = observable.subscribe({});
+        queryManager.mutate({ mutation }).then(() => {
+          observable.setVariables(differentVariables);
+          queryManager.mutate({ mutation }).then(() => {
+            // We have to get out of the Promise scope here
+            // because the promises are capturing the assertion errors
+            // leading to timesouts.
+            setTimeout(assertVariables, 0);
+          });
+        });
+      });
     });
   });
 });