vuejs / apollo

🚀 Apollo/GraphQL integration for VueJS
http://apollo.vuejs.org
MIT License
6.01k stars 521 forks source link

Variables are not reactive (apollo-composable) #951

Open jan-demsar opened 4 years ago

jan-demsar commented 4 years ago

Describe the bug Updating variables does not trigger refetching data in vue-composable as described here: https://v4.apollo.vuejs.org/guide-composable/query.html#variables-ref.

I tried updating value of variable by extracting variables from the query const { result, variables } = useQuery(, passing "ref" and "reactive" variables, but nothing seems to be triggering update.

It works well with Options API and Vue Components but not with Composition API.

To Reproduce I created a sandbox :https://codesandbox.io/s/vue-composable-variables-problem-ozxjf?file=/src/components/Pokemon.vue .

There is an input field and button that triggers updating variable that was passed to query.

Variable updates correctly, but nothing happens with the query.

Expected behavior When changing variables, query should be refetched and new results should be displayed.

Versions vue: 2.6.11 @vue/apollo-composable: 4.0.0-alpha.8 apollo-boost: 0.4.7

Additional context Add any other context about the problem here.

Akryum commented 4 years ago

Try:

useQuery(
      gql`
        query pokemons($first: Int!) {
          pokemons(first: $first) {
            id
            name
          }
        }
      `,
      queryVariables
    )
jan-demsar commented 4 years ago

@Akryum thanks for the reply. I updated the codesandbox with your suggestion, but it is still the same.

Now I see the error:

[Vue warn]: Error in event handler for "hook:updated": "TypeError: Cannot read property 'nextTick' of undefined"

TypeError: Cannot read property 'nextTick' of undefined
    at baseRestart (https://yc7nf.csb.app/node_modules/@vue/apollo-composable/dist/useQuery.js:181:23)
    at restart (https://yc7nf.csb.app/node_modules/@vue/apollo-composable/dist/useQuery.js:213:9)
    at composition_api_1.watch.deep (https://yc7nf.csb.app/node_modules/@vue/apollo-composable/dist/useQuery.js:228:13)
    at applyCb (https://yc7nf.csb.app/node_modules/@vue/composition-api/dist/vue-composition-api.js:938:9)
    at Array.eval (https://yc7nf.csb.app/node_modules/@vue/composition-api/dist/vue-composition-api.js:897:20)
    at flushQueue (https://yc7nf.csb.app/node_modules/@vue/composition-api/dist/vue-composition-api.js:824:21)
    at VueComponent.flushPostQueue (https://yc7nf.csb.app/node_modules/@vue/composition-api/dist/vue-composition-api.js:790:5)
    at invokeWithErrorHandling (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:1859:26)
    at VueComponent.Vue.$emit (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:3885:9)
    at callHook (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:4220:8)
    at callUpdatedHooks (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:4348:7)
    at flushSchedulerQueue (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:4333:3)
    at Array.eval (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:1985:12)
    at flushCallbacks (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:1911:14)
jan-demsar commented 4 years ago

Even if I call refetch method after variable has been changed, query won't load new results.

It only works if I call refetch(updatedVariables).

I have added 2 buttons under the input to test this as well.

Steps to reproduce: 1) Change value of first input 2) Press "Change value" button. That updates queryVariables that are being passed to the query 3) Pressing "Refetch" button calls refetch method which does nothing 4) Pressing "Refetch (pass variables)" button calls refetch(queryVariables) method which updates results (queryVariables is the same constant that has been passed to initial query.

Furthermore I noticed on the local development, that if I enable pooling, it will always fall back to primary variables. So even if I am able to get different results by calling refetch(queryVariables) the moment pool is triggered, variables seems to be reverted.

kyosifov commented 4 years ago

Is there any update on this? I get the same error as @demisolutions whenever my reactive variables change.

kyosifov commented 4 years ago

@Akryum

The problem seems to be this piece of code:

function baseRestart() {
        if (!started || restarting)
            return;
        restarting = true;
        vue_1.default.nextTick(function () {
            if (started) {
                stop();
                start();
            }
            restarting = false;
        });
    }

It seems that if baseRestart is executed a second time the vue_1 variable doesn't have the .default property instead it's the full Vue instance. If you remove .default then everything is working.

kyosifov commented 4 years ago

@demisolutions It seems to be a problem when you use CDN mode of Vue js which makes it a global variable. In my project I was using Vue from vue/dist/vue.js which is the CDN version. Converted it to vue/dist/vue.esm.browser.jsin my webpack and worked.

Take note that If you include Vue in "Node" way like this const Vue = require('vue'); you will have to append the .default to the required, since now you will be using the new module system of ES6. Or you can just do import Vue from 'vue' and it will work fine.

Mando75 commented 3 years ago

@Akryum thanks for the reply. I updated the codesandbox with your suggestion, but it is still the same.

Now I see the error:

[Vue warn]: Error in event handler for "hook:updated": "TypeError: Cannot read property 'nextTick' of undefined"

TypeError: Cannot read property 'nextTick' of undefined
    at baseRestart (https://yc7nf.csb.app/node_modules/@vue/apollo-composable/dist/useQuery.js:181:23)
    at restart (https://yc7nf.csb.app/node_modules/@vue/apollo-composable/dist/useQuery.js:213:9)
    at composition_api_1.watch.deep (https://yc7nf.csb.app/node_modules/@vue/apollo-composable/dist/useQuery.js:228:13)
    at applyCb (https://yc7nf.csb.app/node_modules/@vue/composition-api/dist/vue-composition-api.js:938:9)
    at Array.eval (https://yc7nf.csb.app/node_modules/@vue/composition-api/dist/vue-composition-api.js:897:20)
    at flushQueue (https://yc7nf.csb.app/node_modules/@vue/composition-api/dist/vue-composition-api.js:824:21)
    at VueComponent.flushPostQueue (https://yc7nf.csb.app/node_modules/@vue/composition-api/dist/vue-composition-api.js:790:5)
    at invokeWithErrorHandling (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:1859:26)
    at VueComponent.Vue.$emit (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:3885:9)
    at callHook (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:4220:8)
    at callUpdatedHooks (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:4348:7)
    at flushSchedulerQueue (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:4333:3)
    at Array.eval (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:1985:12)
    at flushCallbacks (eval at Kr (https://codesandbox.io/static/js/sandbox.03d74bce7.js:1:180898), <anonymous>:1911:14)

I am seeing this error, but only when running Jest tests. I don't think @kyosifov's solution will work in this case, as we are using vue-test-utils createLocalVue and not importing Vue anywhere.

We are using mock request handlers in our Jest tests to resolve mock data similar to how @NataliaTepluhina presents it in her Vue Apollo 2020 Testing Guide. However, whenever we update a reactive variable (whether a ref or reactive object), we get the same error as quoted above.

EDIT: A temporary fix for this when using localVue in Jest

  const localVue = createLocalVue();

  // Fix for @vue/apollo-composable in jest environment.
  Vue.default.nextTick = localVue.nextTick;
JuroOravec commented 3 years ago

@Mando75 You are absolutely amazing 💯 💯 We are moving to a similar setup. I came up against this error when transitioning from mocking module imports that wrap individual queries to mocking Apollo query responses with mock-apollo-client. Can confirm that the temporary fix as shown above works!

JuroOravec commented 3 years ago

The issue with Cannot read property 'nextTick' of undefined has been resolved for me. However, I still experience that, when unit testing, then sometimes the queries are not re-triggered when having reactive variables.

I'm unable to pinpoint exactly when this happens, but one of the cases when this happens is when the reactive variables are computed (from @vue/composition-api) values. When using refs, and re-assigning new values inside watch, then it works.

Example:

This does not work:

const queryVariables = computed(() => ({
  var: someRef.value,
}));

const queryOptions = computed(() => ({
  enabled: someRef.value !== undefined,
  fetchPolicy: 'cache-first',
}));

const { result, loading } = useQuery(
  QUERY,
  queryVariables,
  queryOptions,
);

This works:

const queryVariables = ref({});
const queryOptions = ref({});

watch(someRef, (newValue) => {
  queryVariables.value = {
    var: newValue,
  };

  queryOptions.value = {
    enabled: newValue !== undefined,
    fetchPolicy: 'cache-first',
  };
});

const { result, loading } = useQuery(
  QUERY,
  queryVariables,
  queryOptions,
);
vitalykhe commented 3 years ago

i have the same issue.

  1. receiving date as Prop from parent component,
  2. defining query variables as ref with initial value
  3. watching prop update and updating variables inside watch block
  4. query ie expected to re-fetched as described in Docs. But it doesn't
  5. Tried to achieve re-fetch using reactive object, passing variables for query as props as well, but there was no way to solve this

    <script lang="ts">
    
    export default defineComponent({
        name: 'ImagesList',
        props: {
            calendarDate: {
                type: String,
                required: true
            }
        },
        setup(props) {
          const query....(something)
          const {calendarDate} = toRefs(props)
    
          const variables = ref({
              date: new Date().toISOString()
          })
    
          watch(calendarDate, (newValue : string) : void => {
           variables.value = {
                date: new Date(newValue).toISOString()
            }
          })
    
          const {result, loading, error} = useQuery(query, variables)
    
          const expectedResults = useResult(result, null)
    
          return {timecardsForDay, loading, error, IMAGE_URL_PATH}
        }
    })
    </script>

package.json "@apollo/client": "^3.3.9", "@quasar/extras": "^1.0.0", "@vue/apollo-composable": "^4.0.0-alpha.12", "@vue/composition-api": "^0.6.4", "apollo-cache-inmemory": "^1.6.6", "apollo-client": "^2.6.10", "apollo-link": "^1.2.14", "apollo-link-http": "^1.5.17", "axios": "^0.18.1", "core-js": "^3.6.5", "graphql": "^15.5.0", "graphql-tag": "^2.11.0", "node-fetch": "^2.6.1", "quasar": "^1.0.0", "vue-apollo": "^3.0.7", "vue-class-component": "^7.2.6", "vue-fragment": "^1.5.1", "vue-i18n": "^8.0.0", "vue-property-decorator": "^9.1.2", "vuex-typex": "3.1.6"

reaink commented 2 years ago

I also have this problem

"@apollo/client": "^3.5.10", "@vue/apollo-composable": "^4.0.0-alpha.16",

nikzanda commented 1 year ago

I also have this problem.

"@apollo/client": "^3.7.15", "@vue/apollo-composable": "^4.0.0-beta.7",

JagathPrasad commented 1 month ago

I am also facing same issue in "@apollo/client": "^3.10.4", "@vue/apollo-composable": "^4.0.2",