Coder Social home page Coder Social logo

Cookbook for typed Vuex modules about typescript HOT 25 CLOSED

nuxt avatar nuxt commented on May 10, 2024 15
Cookbook for typed Vuex modules

from typescript.

Comments (25)

danielroe avatar danielroe commented on May 10, 2024 4

I've used a number of different modules, and particularly like the class-based approach, but have continued to bump up against issues, such as:

  • too much boilerplate for accessors
  • incompatibilities with Nuxt
  • not being able to access this.$axios or the store instance within the store

I've decided to try a different approach and have just released a package that tries to keep things as vanilla as possible, producing a strongly typed accessor from standard Nuxt store & submodule files: nuxt-typed-vuex.

I'd welcome any thoughts & contributions: see npm & git.

Note: I realise this is probably temporary until the release of Vue 3. Along with @kevinmarrec I'd be very interested in an approach using the new Composition API.

from typescript.

kevinmarrec avatar kevinmarrec commented on May 10, 2024 3

Hey here, thanks to @danielroe we have a first version of the Store Cookbook : https://typescript.nuxtjs.org/cookbook/store.html

from typescript.

rpiosi avatar rpiosi commented on May 10, 2024 1

If anyone is interested in a type-safe store accessed from components, here are useful decorators with easy to follow instructions about usage with Nuxt: vuex-module-decorator

from typescript.

kevinmarrec avatar kevinmarrec commented on May 10, 2024 1

@robertpiosik I think I had a good setup working with it, still I'm not a fan of Class API anymore cause I really fell in love with the new Composition API and fills the lack/issues that Class API would have (see https://vue-composition-api-rfc.netlify.com/#type-issues-with-class-api)

Still, I would prefer someone working on this and submit a PR I could review :)

There's anyway same work to do around the Options & Composition API.

from typescript.

evald24 avatar evald24 commented on May 10, 2024 1

Today I was thinking how to do it.
I did it like this:

// ~/store/user.ts

import { User } from '~/@types';

export const state = (): { user: User | null } => ({
  user: null
});

export const mutations = {
  SET:
    (state, payload: User | null) => {
      state.user = payload;
    }
};

export const actions = {
  set:
    ({ commit }, payload: User | null) => {
      commit('SET', payload);
    }
};

export const getters = {
  get:
    state => {
      return state.user;
    }
};
// ~/@types/store.d.ts

import { User } from '~/@types';
import { Store, DispatchOptions, Payload, Dispatch } from 'vuex/types/index';

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

export interface MyStore extends Omit<Store<any>, 'getters'> {
  readonly getters: {
    'user/get': User;
    // ... and other
  };
  dispatch: MyDispatch;
}

interface MyDispatch extends Dispatch {
  (type: 'user/set', payload: User | null, options?: DispatchOptions): Promise<any>;
  // ... and other
}

Dispatch

dispatch-0
dispatch-1

Getters

getters-0
getters-1

It is not perfect, but at least something.

from typescript.

lmanzke avatar lmanzke commented on May 10, 2024 1

Okay, sorry, then I misunderstood you. Just had a similar issue when using vuex-module-decorators that this.$store threw an exception. The reason was a missing "@component" annotation on my class.

from typescript.

kevinmarrec avatar kevinmarrec commented on May 10, 2024 1

Nice work @danielroe !

Well, in fact the idea with Composition API is about using provide and inject, there is something in the RFC documentation here : https://vue-composition-api-rfc.netlify.com/#usage-alongside-existing-api.

I think state management around Vue 3 is gonna be much less opinionated, as your state could just be a reactive object.

It could be used like that :

const { todos, addTodo, removeTodo } = useStore()

The idea is having useStore always returning the same reactive state in all components, and that's what provide (around root component) and inject (around children ~ which mean can be done inside useStore function definition directly) are useful for :)

from typescript.

piotrek-horodenski avatar piotrek-horodenski commented on May 10, 2024 1

if I may, let me give some suggestions:

  • it should contain nuxt + typescript + vuex-module-decorators setup with example store and configuration for SSR
  • usage of stores in components ideally should be like:
import {
  Component,
  Vue,
} from 'nuxt-property-decorator'

import { AuthStore } from '@/store'
import { LoginMessage } from '@/store/auth'
import { apiUrl } from '@/utils/env'

@Component
export default class LoginPage extends Vue {  
  identifier: string = '';
  password: string = '';

  login(msg: LoginMessage) {
    console.dir(AuthStore)
    AuthStore.login(msg)
  }

  get apiURL() {
    return apiUrl
  }
}
  • store should look sth like that
import {
  Module,
  VuexModule,
  Mutation,
  Action
} from 'vuex-module-decorators'

import { $axios } from '@/utils/axios'
import { apiUrl } from '@/utils/env'

@Module({
  name: 'auth',
  stateFactory: true,
  namespaced: true,
  namespacedPath: 'auth',
})
export class AuthStoreClass extends VuexModule {
  user?: User;
  logging: boolean = false;
  token?: string;
  error?: AuthError;

  get isLoggedIn(): boolean {
    return undefined !== this.user
  }

  get hasToken(): boolean {
    return undefined !== this.token
  }

  @Mutation
  setLogging(value: boolean) {
    this.logging = value
  }

  @Mutation
  setError(error?: AuthError) {
    this.error = error
  }

  @Mutation
  setUser(user: User) {
    this.user = user
  }

  @Mutation
  setToken(token?: string) {
    this.token = token
  }

  @Action
  async login(message: LoginMessage) {
    this.setLogging(true)
    this.setToken(undefined)
    this.setError(undefined)
    try {
      const loginResponse = await $axios.$post(
        apiUrl + 'auth/local', 
        message
      )
      this.setUser(loginResponse.user)
      this.setToken(loginResponse.jwt)
    } catch (error) {
      this.setError(error)
    }
    this.setLogging(false)
  }
}

export interface User {
  username: string;
}

export interface AuthError {
  message: string;
  code: number;
}

export interface LoginMessage {
  identifier: string;
  password: string;
}

from typescript.

noopurphalak avatar noopurphalak commented on May 10, 2024 1

@piotrek-horodenski is you index.ts same as given here? https://typescript.nuxtjs.org/cookbook/store.html#class-based

from typescript.

kevinmarrec avatar kevinmarrec commented on May 10, 2024

Does anyone have found some best practices he would want to share when dealing with Options API ?

For Class API I would also like to know which library you found the more stable for Vuex modules with classes & decorators, as I know there are bunch of them.

Thanks in advance.

Also I tagged the issue as help wanted & good first issue, any help (includes answers to this comment & Pull requests) resolving the lack of Vuex examples would be much appreciated !

from typescript.

rpiosi avatar rpiosi commented on May 10, 2024

Hey @kevinmarrec

For Class API I would also like to know which library you found the more stable for Vuex modules with classes & decorators, as I know there are bunch of them.

Can you mention a few?;) vuex-module-decorator really makes the job done.

from typescript.

lmanzke avatar lmanzke commented on May 10, 2024

@danielroe If this.$axios or this.$store does not work, it seems you lack the @component decorator on the class. At least that's what made it work for me :).

from typescript.

danielroe avatar danielroe commented on May 10, 2024

@lmanzke Which package are you referring to? I've tried several class-based packages. Not all suffered from that particular limitation. In particular there is an ongoing issue with vuex-module-decorators that needs elaborate workarounds, despite being an otherwise excellent package.

I've decided to try a completely different approach in nuxt-typed-vuex: vanilla Vuex stores with strongly-typed accessors which should work better for more complex scenarios.

Edit: The limitation I'm talking about is not being able to access this.$axios within the store, not within a component.

from typescript.

kevinmarrec avatar kevinmarrec commented on May 10, 2024

The thing is that with Composition API, you don't have to care about doing something more to have it type-safe. It's TypeScript friendly and gonna make for sure TypeScript users IMO migrating fast to this new API.

from typescript.

steklopod avatar steklopod commented on May 10, 2024

Unfortunately, Class-based vuex-module-decorators example does not work. I couldn’t get everything to work using CTRL + C + CTRL + V from vuex-module-decorators.

You have 5 steps, but it seems to be not enough :-(

from typescript.

danielroe avatar danielroe commented on May 10, 2024

@steklopod Those aren't steps to getting it working - just Nuxt-specific key points to remember whilst following the instructions from vuex-module-decorator's own setup guide. For example, you need to configure your TS to use experimental decorators, transpile vuex-module-decorators, etc. (Maybe it should be more of a how-to guide - what do you think @kevinmarrec?)

Be that as it may, is there anything particular that you think should be in the cookbook? What do you think would be helpful to include?

from typescript.

piotrek-horodenski avatar piotrek-horodenski commented on May 10, 2024

Managed to make it works, missing default in auth.ts

from typescript.

piotrek-horodenski avatar piotrek-horodenski commented on May 10, 2024

also, get rid off question marks in vuex classes, use user: User | null = null; notation and use null value, not undefined

from typescript.

mdaffan avatar mdaffan commented on May 10, 2024

I Have Vuex as Modules way in NUXT and i have my mutations, actions, getters in different files can someone please help me in getting vuex-module decorator to work with my folder structure I don't want to have everything in my store/index.ts I want it to be modularized like the way I have it now mutations, actions, getters in different files and getting imported in index.ts.

from typescript.

danielroe avatar danielroe commented on May 10, 2024

@mdaffan I don't think vuex-module-decorator would be your best option, if you want to preserve that structure. You might be able to make it work with extends but I'd really advise against it.

from typescript.

JanusSpark avatar JanusSpark commented on May 10, 2024

How can I use the store data(some user info) in my client-report-plugin(out of vue component) with typed-vuex?
When I try to use Vue.prototye.$accessor,it returned an undefined.
And When I try to get Vue.prototye.$accessor in setTimeout,it throw an error: "Uncaught TypeError: Cannot read property '$options' of undefined"
@danielroe Do you have a good idea?

from typescript.

danielroe avatar danielroe commented on May 10, 2024

@JanusSpark Very happy to help 🙂 Could you raise an issue at https://github.com/danielroe/typed-vuex with a minimal repro from https://template.nuxtjs.org? I'll get to it straight away 👍

from typescript.

ryanmstokes avatar ryanmstokes commented on May 10, 2024

@mdaffan I don't think vuex-module-decorator would be your best option, if you want to preserve that structure. You might be able to make it work with extends but I'd really advise against it.

Hi @danielroe Is there a way to preserve that structure (actions.ts, mutations.ts, getters.ts etc) using your typed-vuex library in nuxt please? Thanks!

from typescript.

danielroe avatar danielroe commented on May 10, 2024

@ryanmstokes Yes, it's possible to preserve that structure 👍 Just import them from those locations when defining the store:

https://typed-vuex.roe.dev/getting-started-nuxt#defining-the-accessor-type

import { getAccessorType } from 'typed-vuex'

import getters from '~/store/getters'
import state from '~/store/state'
// etc.

export const accessorType = getAccessorType({
  state,
  getters,
  mutations,
  actions,
  modules: {
    // The key (submodule) needs to match the Nuxt namespace (e.g. ~/store/submodule.ts)
    submodule: {
      // ... you can pass getters, state, etc. from separate imports here too
    }
  },
})

from typescript.

ryanmstokes avatar ryanmstokes commented on May 10, 2024

@ryanmstokes Yes, it's possible to preserve that structure 👍 Just import them from those locations when defining the store:

https://typed-vuex.roe.dev/getting-started-nuxt#defining-the-accessor-type

import { getAccessorType } from 'typed-vuex'

import getters from '~/store/getters'
import state from '~/store/state'
// etc.

export const accessorType = getAccessorType({
  state,
  getters,
  mutations,
  actions,
  modules: {
    // The key (submodule) needs to match the Nuxt namespace (e.g. ~/store/submodule.ts)
    submodule: {
      // ... you can pass getters, state, etc. from separate imports here too
    }
  },
})

Thank you so much for replying so quickly! thats awesome will try that out now!

from typescript.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.