Search code examples
reactjsvuejs2vuex

Is there react/redux like mapStateToProps way in Vue/Vuex


Usual way to map state and actions in React/Redux looks something like this, so mapping functions are placed separately from component code:

import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

import myAction from 'actions/request';

class MyComponent extends Component {
  /* BODY */
}

function mapStateToProps(state) {
  return {
    myComponentProp: state.myReducer.myReducerProp
  };
}

function mapDispatchToProps(dispatch) {
  return {
    myComponentPropAction: bindActionCreators(myAction, dispatch),
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);

The only described way to map state and actions I have found in Vue looks like this

import { mapState, mapActions } from 'vuex';

export default {
  computed: {
    ...mapState('myReducer', {
      myComponentProp: (state) => state.myReducerProp,
    }),
    ...{
      /* aditional manipulations with this.myComponentProp */
    }
  },
  methods: {
    ...mapActions('myReducer', [
      'myReducerAction'
    ]),
    ...{
      myEventHandler: function() {
        /* checke conditions before action call */
        this.myReducerAction();
      },
    }
  }
}

Because of the number of spreading, the code looks fuzzy, so the question is: Is there a way to move mapState and mapActions outside component like in react/redux usual approach.

Thanks for help!


Solution

  • Okay so along with typescript support they also added in a vue-class-component decorator which could be used to achieve what your after. The link to the repository for this can be found here but I would suggest instead creating a new project via the CLI and going from there as it was added in v3 Vue Class Component Github Repository.

    <script>
    
    function Getter (getterType) {
      return createDecorator((options, key) => {
        if (!options.computed) options.computed = {}
        options.computed[key] = function () {
          return this.$store.getters[getterType]
        }
      })
    }
    
    import Vue from 'vue'
    import Component from 'vue-class-component'
    
        @Component({
          props: {
            propMessage: String
          }
        })
        export default class App extends Vue {
        @Getter('foo') bar
        @Setter('psu') psi
    
          // computed
          get computedMsg () {
            return 'computed ' + this.msg
          }
    
          // method
          greet () {
            alert('greeting: ' + this.msg)
          }
        }
        </script>
    

    As you can see were calling in our getters and setters using a function here which is less than optimal but is closer to a succulent answer. Now in comes the vuex-class-binding package which abstracts all of those murky functions: vuex class

    import Vue from 'vue'
    import Component from 'vue-class-component'
    import {
      State,
      Getter,
      Action,
      Mutation,
      namespace
    } from 'vuex-class'
    
    const someModule = namespace('path/to/module')
    
    @Component
    export class MyComp extends Vue {
      @State('foo') stateFoo
      @State(state => state.bar) stateBar
      @Getter('foo') getterFoo
      @Action('foo') actionFoo
      @Mutation('foo') mutationFoo
      @someModule.Getter('foo') moduleGetterFoo
    
      // If the argument is omitted, use the property name
      // for each state/getter/action/mutation type
      @State foo
      @Getter bar
      @Action baz
      @Mutation qux
    
      created () {
        this.stateFoo // -> store.state.foo
        this.stateBar // -> store.state.bar
        this.getterFoo // -> store.getters.foo
        this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
        this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true })
        this.moduleGetterFoo // -> store.getters['path/to/module/foo']
      }
    }
    

    This is there example and it's really nice because were able to take a namespaced module and call all of it's getters and setters without any nasty custom functions and we can import all of that ready to use into a const like above. Now you'd have access to all of your modules functionality using just decorators. This is as close as it really gets to being able to assign your functionality into the component sadly, but it looks pretty nice once you've got it all setup. You can do this with or without TS I think but I've always done it in TS as it has the first class support for the vue class components which are still relatively new.