How to use mapActions with vue + Typescript class component?
Asked Answered
B

4

11

I would like to know whats the correct way of using ...mapActions([]) within a Typescript vue class component.

this is how I do it:

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import { mapActions } from "vuex";

@Component
export default class UsersAdd extends Vue {
  userName: string = "";

    ...mapActions(["newUser"]) /*mapAction from vuex */
  addUser() {
    console.log("...adding new user");
    this.newUser(this.userName);
  }
}
</script>

and as you can tell it's not working...

with Javascript I do it this way.

methods:{
  ...mapActions(["newUser"])
}

How can I do it with Typescript class component?

Edit: I have tried it this way, but it's still not working

@Component({
  methods: {
    ...mapActions(["newUser"]) /*mapAction from vuex */
  }
})
export default class UsersAdd extends Vue {
  userName: string = "";

  addUser() {
    console.log("...adding new user");
    this.newUser(this.userName);
  }
}
Belshin answered 6/2, 2020 at 16:16 Comment(3)
The syntax is invalid. There won't be a good way because TS will be unaware of the methods you're trying to add. See github.com/vuejs/vue-class-component/issues/… for a workaround.Belgrade
@EstusFlask the link you share will work with mapGetters but if I use methods: mapActions([]) and then call it inside addUser() it wont workBelshin
Does it cause type error? You need to type methods manually as well, see charts!: any[] in the link. I suppose in your case it needs to be newUser!: (username: string) => void.Belgrade
T
13

If you don't want to bring in yet another dependency (i.e. vuex-class). We can map actions in the store to the component in Typescript as this:

@Component({
  computed: {
    ...mapActions({
      someLoadDataAction: "someLoadDataAction"
    })
  }
})
export default class HelloWorld extends Vue {
  someLoadDataAction!: () => any;

  mounted() {
    console.log(this.someLoadDataAction);
  }
}

The key is adding this someLoadDataAction!: () => any for static type check for Typescript. As an example, we have the function type defined as () => any, but you can change it to the actual return type from the action in your vuex store.

Thrice answered 24/5, 2020 at 22:0 Comment(1)
I tried to pass a string as a parameter to someLoadDataAction, but it catches an unknown object in the variable inside the method someLoadDataAction?Swig
N
6

With help of Action annotation you can do this in that way. Installation npm install --save vuex-class

import { Action } from 'vuex-class'

@Component()
export default class UsersAdd extends Vue {
  userName: string = "";

  @Action('newUser')
  newUser!: (newUser: string) => void
  addUser() {
    console.log("...adding new user");
    this.newUser(this.userName);
  }
}
Nava answered 30/4, 2020 at 13:25 Comment(0)
B
1

Other option, you can use vuex-module-decorators which is better support typescript and reduce code.

Install

npm install -D vuex-module-decorators

store/index.ts

import { Action, getModule, Module, Mutation, VuexModule } from "vuex-module-decorators";
import store from "..";

// define Mutations name
enum Mutations {
  NEW_USER_NAME = "NEW_USER_NAME"   
}

// name: module name
// dynamic: true,dynamic create module
// namespaced: true,using namespaced
// store: your root store/index.ts
@Module({ name: 'UserStore', dynamic: true, namespaced: true, store })
export default class UserStore extends VuexModule {
  // state
  userName = '';

  // mutation
  @Mutation
  [Mutations.NEW_USER_NAME](userName: string) {
    this.userName = userName;
  }

  // action
  @Action
  newUser(userName: string) {
    this.context.commit(Mutations.NEW_USER_NAME, userName);
  }
}

// use getModule to safe get instance
export const userStore = getModule(UserStore);

UsersAdd.vue

import { userStore } from "@/store/modules/user";
@Component()
export default class UsersAdd extends Vue {
  userName: string = "";
   
  addUser() {    
    console.log("...adding new user");     
    userStore.newUser(this.userName);
  }
}
Boulanger answered 19/7, 2021 at 3:5 Comment(0)
P
1

One other way could be to use custom decorator, which would replace a method in your component with a method from the vuex store.

function VuexAction(moduleName: string): any {
  return createDecorator((options: any, key: string) => {
    if (!options.methods) options.methods = {};
    options.methods[key] = function wrapperMethod(...args: any[]) {
      return this.$store._actions[`${moduleName}/${key}`][0](...args);
    };
  });
}

The decorator can be used like this:

@Component
export default class SomeComponent extends Vue {
  @VuexAction("vuexModuleName") vuexModuleActionName!: () => void;

  async doStuff () {
    this.vuexModuleActionName();
  }
}

The idea is based on docs about custom decorators:

https://class-component.vuejs.org/guide/custom-decorators.html

And a custom vuex getter decorator:

https://github.com/vuejs/vue-class-component/issues/56#issuecomment-272604193

Piccard answered 1/10, 2021 at 15:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.