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

Child component using Vue.extends not receiving global $state property from Vuex #382

Closed
chriscasola opened this issue Jan 27, 2018 · 8 comments

Comments

@chriscasola
Copy link
Contributor

Version

1.0.0-beta.10

Reproduction link

https://jsfiddle.net/7t28dy1u/

Steps to reproduce

Run the fiddle - console error because this.$store is undefined on the child component.

What is expected?

All child components should receive the global $store mixin from Vuex.

What is actually happening?

Only the root element is receiving the $store property.


I can fix the jsbin by removing the Vue.extend notation and just passing the raw javascript objects into mount. I also confirmed that this works as expected when using Vue to construct the component normally (not using vue-test-utils).

I ran into this because I was using ES6 imports to import my store into all my app components. I switched to using this.$store which works fine in the app, but broke a lot of my unit tests that were using vue-test-utils to mount the components.

@chriscasola
Copy link
Contributor Author

I think this is happening because the constructor of the child component is not extending from the localVue which has the Vuex plugin, so when the child component is constructed the beforeCreate hook that Vuex uses is not present. I'm not too sure how to fix this, if someone can point me in the right direction I may be able to.

@eddyerburgh
Copy link
Member

As you said, the problem is that you're creating the component with Vue.extend.

There are a few options I can think of to fix this.

First, we could fix the stub option to compile template functions (I think we should do that anyway), then leave it to the user to call localVue.extend on the component and pass it as a stub:

mount(Component, {
  subComponent: localVue.extend(subComponent)
})

Second, we could call localVue.extend on every component in the tree if you use localVue. I don't think we should do this.

Third, we could add an option that would call localVue.extend on every child component.

@chriscasola
Copy link
Contributor Author

I'm calling Vue.extend because that's the only way for typescript to work properly with the Vue component definition.

I don't think the first option, of leaving it the user to call localVue.extend is really feasible, because that code is in source files, not test files. You'd have to import the component into the test, and then recurse through the whole components tree and wrap them in localVue.extend.

@hmsk
Copy link

hmsk commented Apr 3, 2018

Great to see you, Edd in VueConf.US 😄
I'm facing similar troubles with Vuex $store, vue-i18n $t and TypeScript.

I agree with Edd's third option if the second option is too much for non-extend way.

mount(Component, {
  localVue,
  extendChildren: true
})

Ideally, don't want to have the weird option. I wonder if we could detect whether Component of mount(Component, {... is by extend or not, could handle child components automatically 🤔

@Perogy
Copy link

Perogy commented Apr 10, 2018

I am also having troubles with this. LocalVue does not seem capable of passing the $store down to subcomponents. Shallow works fine. I guess I will just stick to doing tests with shallow rendering for now.

@eddyerburgh eddyerburgh changed the title Child component not receiving global $state property from Vuex Child component using Vue.extends not receiving global $state property from Vuex May 9, 2018
@eddyerburgh
Copy link
Member

We now extend all extended child components automatically

@hmsk
Copy link

hmsk commented May 11, 2018

That's awesome, thanks for working on that, Edd!

@mediafreakch
Copy link

mediafreakch commented Feb 28, 2020

I think I do observe exactly the problem mentioned in this issue with Async Recursive Components in a Webpack environment...

An abstract of the setup:

// GenericView.ts
export default Vue.extend({
  name: 'GenericView',
  props: ['kind'],
  render: function(h) {
    switch (this.kind) {
      case 'Chart':
        return h(Chart)
      case 'Dashboard':
        return h(Dashboard)
   }
  }
})

// Dashboard.ts
export default Vue.extend({
  name: 'Dashboard',
  components: {
    GenericView: () => import('@/src/components/GenericView')
  },
 template: '<GenericView kind="Chart" />'
})

When testing GenericView and injecting a store, the store is available in the first render of the component, but not in the render for the GenericView loaded by the Dashboard component...

My workaround for now is to stub the GenericView, as paradox as it sounds... This way it will render with the store the first time, but will be stubbed out the second time.

UPDATE:
Figured that it might not be an issue with vue-test-utils, but vue-testing-library: testing-library/vue-testing-library#122

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants