Search code examples
javascriptvue.jsvue-componentvuexvue-props

Pass state property via props (Vuex)


I have a component that should display data from the store, but the component is reusable, so I would like to pass the name of the store module and property name via props, like so:

<thingy module="module1" section="person">

Then, in the component:

<template>
  <h2>{{ title }}</h2>
  <p>{{ message }}</p>
</template>

<script>
import { mapState } from 'vuex';
import get from 'lodash.get';

export default {
  props: [
    'module',
    'section',
  ],
  computed: mapState(this.module, {
    title: state => get(state, `${this.section}.title`),
    message: state => get(state, `${this.section}.message`),
  })
}
</script>

The problem is, it seems the props are undefined at the time when mapState() is executed. If I hardcode the prop values, the component works. Also, if I log the props in the created() hook, I get the expected values. So it seems like a race condition.

Am I going about this the wrong way here?

Update

The module namespace must be passed from within the mapping function, like so:

computed: mapState({
  title() {
    return get(this.$store.state, `${this.module}.${this.section}.title`);
  },
  message() {
    return get(this.$store.state, `${this.module}.${this.section}.message`);
  }
})

(note that get() is a lodash, not a vue function)

This can be further abstracted into a mixin.


Solution

  • Note the comment in the mapState example:

    // to access local state with `this`, a normal function must be used
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
    

    You are using arrow functions.

    As for this.module, I think you're going to have to forego the binding helper notation and explicitly put the module reference into the definitions. I'm guessing that looks like:

    computed: mapState(this.module, {
      title(state) {
        return get(`${state}.${this.module}`, `${this.section}.title`);
      },
      message(state) {
        return get(`${state}.${this.module}`, `${this.section}.message`);
      }
    })