Andre12
Andre12

Reputation: 564

Accessing Vuex store module's state inside router.ts

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?

Upvotes: 12

Views: 4764

Answers (5)

Lervad
Lervad

Reputation: 167

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
  }
}

Upvotes: 1

Frank
Frank

Reputation: 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)

Upvotes: -1

StevenSiebert
StevenSiebert

Reputation: 1426

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;

Upvotes: -1

Wyf
Wyf

Reputation: 7

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

Upvotes: -1

Danijel
Danijel

Reputation: 1185

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.

Upvotes: -1

Related Questions