Accessing Vuex store module's state inside router.ts
Asked Answered
W

5

12

I followed this tutorial to set up a Vuex store with modules using TypeScript.

So far I have:

vuex/types.ts:

export interface RootState {
    version: string;
}

vuex/user-profile.ts:

import { ActionTree, Module, MutationTree } from 'vuex';
import { RootState } from './types';

interface User {
    firstName: string;
    uid: string;
}

interface ProfileState {
    user?: User;
    authed: boolean;
}

const state: ProfileState = {
    user: undefined,
    authed: false,
};

const namespaced: boolean = true;

export const UserProfile: Module<ProfileState, RootState> = {
    namespaced,
    state,
};

store.ts:

import Vue from 'vue';
import Vuex, { StoreOptions } from 'vuex';
import { UserProfile } from '@/vuex/user-profile';
import { RootState } from '@/vuex/types';

Vue.use(Vuex);

const store: StoreOptions<RootState> = {
  state: {
      version: '1.0.0',
  },
  modules: {
      UserProfile,
  },
};

export default new Vuex.Store<RootState>(store);

In my router.ts I want to access the authed state of the store like this:

import store from './store';
//...other imports...

const router = new Router({
//... route definitions...
});

router.beforeEach((to, from, next) => {
  const isAuthed = store.state.UserProfile.authed;
  if (to.name !== 'login' && !isAuthed) {
    next({ name: 'login' });
  } else {
    next();
  }
});

The code works (the app redirects properly), HOWEVER, the compiler throws errors saying Property 'UserProfile' does not exist on type 'RootState', which makes sense since it's not defined, but should it not look under the modules as well, or did I not define the module correctly?

Weswesa answered 17/10, 2018 at 20:30 Comment(2)
Have you tried adding the UserProfile in the RootState interface ?Monologue
There is a alternative way to get the Module in the Vuex, you can get it using the getModule method from the "vuex-module-decorators" libMonologue
M
1

You need to define all stores to rootState interface like this:

export default interface RootState {
  version: string,
  UserProfile: any
}

You could also import the interface from the UserProfile and use instead of any. Since you dont use pascal casing for your file names

This will tell the rootState to expect a vuex store called UserProfile with any type or UserProfile interface.

I have a rootstate interface for my vuex store like this:

export default interface RootState {
  version: string,
  config: any,
  cart: any,
  checkout: any,
  cms: any,
  product: any,
  shipping: any,
  user: any,
  wishlist: any,
  attribute: any,
  ui: any,
  newsletter: any,
  category: {
    current_path: string,
    current_product_query: any,
    current: {
      slug: string,
      name: string
    },
    filters: any
  }
}
Milestone answered 9/3, 2023 at 9:11 Comment(0)
P
-1

EDIT: It seems that direct access to state is the issue here. Line

const isAuthed = store.state.UserProfile.authed;

I believe this happens because it's namespaced. The solution would be to create a getter.

const getters: GetterTree<ProfileState, RootState> = {

    user(state): User {
        return state.user
    }

};

And then you can access it like

store.getters['UserProfile/user']

Also, please consider using getters for accessing your state data. See Getters for reference.

Puerility answered 19/10, 2018 at 10:57 Comment(6)
I do have RootState in my module: export const UserProfile: Module<ProfileState, RootState> Weswesa
Yes, but did you add GetterTree, MutationTree etc? I believe those should also be added and exported even when empty.Puerility
I added all the code (getters, mutations, actions, etc. etc.) same issueWeswesa
I edited the answer. Could you please also try exporting the main store empty mutations, actions, and modules at the end? I vaguely remember coming across this issue and solved it like that at the beginning.Puerility
i did, same issue persistsWeswesa
This is not how getters are supposed to be used, store references are already reactive.Fronton
J
-1

1

     const isAuthed = store.state["UserProfile"].authed; // false

2

    const state:any|State = store.state
    const isAuthed = state.UserProfile.authed; // false

3

    const isAuthed = (<any|State>store.state).UserProfile.authed; // false
Jackiejackinoffice answered 27/2, 2019 at 11:44 Comment(1)
1 has the same problem. 2 and 3 work, but casting them as any sort of negates the purpose of typescript. upvoted.Weswesa
D
-1

This is a working solution with dependecies "vuex": "^4.0.2", "vue-router": "^4.0.10" and "vue": "^3.1.4".

Import the store into your router file to access your state in the module:

import store from "@/store/store";

In my case the modules name is authModule and I access the state of token, where a jwt is stored:

let loggedIn = store.state.authModule.token;
Dreamadreamer answered 3/9, 2021 at 7:32 Comment(3)
Your answer is missing the key information: how did you type store so that you can access store.state.authModule?Flavin
It is not a trick question. You demonstrate how you import store and how you access it, but you don’t show what type you assigned to store. This is crucial in order to answer this question because that is the problem: how to type a store so that it allows access of module state without error. Also, vuex does not provide store as you import it.Flavin
@Flavin Sorry, but just asking "how did you type store so that you can access ..." show missing knowledge. You don't "type" it in a special way and it was not necessary to explain how to import a module in vue.js, as the questioning user according to "import store from './store';" already know how to do it. This is not part of the question.Dreamadreamer
B
-1

I would recommend doing a double cast. One to any and one back to what you actually want. Remember that in the last router.ts file below, "store" is an instance of the Store and the other two imports are just types.

I have omitted the namespaced, state, getters, actions, mutations code for brevity. They are simply object structures

store/myModule/types.ts:

export default interface State {
    testValue
}

store/myModule/index.ts:

import RootState from '../types'

const module: Module<State, RootState> = {
    namespaced,
    state,
    getters,
    actions,
    mutations,
}

export default module

store/types.ts:

interface RootState {
  myOtherTestValue: string
}

export default RootState
export { RootState }

store/index.ts:

import RootState from './types'
import myModule from './myModule'

export const cmStore: StoreOptions<RootState> = {
    actions,
    mutations,
    state,
    modules: {
        myModule,
    },
    plugins,

}

export default new Vuex.Store<RootState>(cmStore)

in router.ts:

import store from './store'
import RootState from './store/types'
import MyModuleState from './store/myModule/types'

// to get what you want typed how you want:
const myState = ((store.state as any).myModule as MyModuleState)

console.log(myState.testValue)
Balm answered 23/11, 2021 at 3:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.