danielroe/typed-vuex

docs: not able to get it working

lucasctd opened this issue ยท 13 comments

๐Ÿ“š Is your documentation request related to a problem? Please describe.
I did everything that is in your documentation but it does not seem to work :v
I have a typed attribute in my state which has some methods that I need to use in my component, but when I read the object through the $accessor or the $store itself, it's just a plain object.

I have no idea of what I am doing wrong :/

Here is the interface

export interface Jacket {
    getFront(): JacketFront
    getBack(): JacketBack
    getLeft(): JacketLeftSide
    getRight(): JacketRightSide
}

and here is my state (index.ts)

import { GetterTree, ActionTree, MutationTree } from 'vuex'
import { getAccessorType } from 'typed-vuex'
import { Customer } from '~/data/customer'
import { Tab } from '~/data/tab'
import { Jacket, defaultJacket } from '~/data/jacket/jacket'
import { EconomicJacket } from '~/data/jacket/economicJacket'

export const state = () => ({
    customer: Customer.genEmptyInstance() as Customer,
    currentTab: new Tab('customer', 'Customer') as Tab,
    tabs: [
        new Tab('customer', 'Customer'),
        new Tab('jacket-style', 'Jacket Style'),
    ] as Tab[],
    jackets: [] as Jacket[],
    currentJacket: defaultJacket as Jacket
})

export type RootState = ReturnType<typeof state>

export const getters: GetterTree<RootState, RootState> = {
}

export const mutations: MutationTree<RootState> = {
    SET_CUSTOMER: (state: RootState, customer: Customer) => {
        state.customer = customer
    },
    SET_CURRENT_TAB: (state: RootState, tab: Tab) => {
        state.currentTab = tab
    },
    SET_JACKETS: (state: RootState, jackets: Jacket[]) => {
        state.jackets = jackets
    },
    SET_CURRENT_JACKET: (state: RootState, jacket: Jacket) => {
        state.currentJacket = jacket
    },
}

export const actions: ActionTree<RootState, RootState> = {
    async loadJackets ({ commit }) {
        const jackets: Array<EconomicJacket> = await this.$axios.$get('/json/economic/jackets.json', { responseType: 'json' })
        commit('SET_JACKETS', jackets.map(j => EconomicJacket.clone(j)))
    }
}

export const accessorType = getAccessorType({
    state,
    getters,
    mutations,
    actions,
    modules: {
    },
})

so in my component I am trying to call a method

this.$accessor.currentJacket.getFront()

but I always get tsthis.$accessor.currentJacket.getFront is not a function

if I call this method inside the store, it will work, the problem is when the data is passed to the component.

never mind, just disabled SSR and it worked xD

@lucasctd You shouldn't need to disable SSR - and in fact this shouldn't affect the typing of the accessor. Would you provide a reproduction?

I may provide you on Thursday, I have to deliver the project tomorrow so I am in a hurry xD

About the SSR thing, I set my nuxt project target to "static". They may not work together I guess since the "static" is a client only project.

I don't know too much about how SSR works though, so pls correct me if I am wrong on something I said above XD

according to the docs

https://nuxtjs.org/docs/2.x/configuration-glossary/configuration-target

the target should be:

'server': For server side rendering
'static': For static sites

so I think yeah, it does not make sense to let SSR true for static websites.

@lucasctd Both server and static targets involve SSR (one is per-request, and the other is at generate time). Unless you have specifically set ssr: false (which I wouldn't recommend), Nuxt will still server-render your pages with static sites.

When using SSR, Nuxt stringifies your store state into an object and then passes this to the frontend/client within the HTML. So if you are expecting an object in the initial store data (currentJacket) to have a function as a property, it won't work with server-rendering - which likely explains the issue you're experiencing.

@danielroe it's probably the reason why I was getting
WARN Cannot stringify arbitrary non-POJOs Navigation
in my console.
So what you are saying is that, since I need the typed object I can't use SSR, right?

@lucasctd I would just use a getter for currentJacket that adds the correct function as a property.

so you mean casting the plain object to a typed one in the Getter?

also, sorry for bother you but I tried to create modules as you did in here, but now I am getting unknown action type: loadJackets xD

the store:

import { getAccessorType, mutationTree, actionTree } from 'typed-vuex'
import { Tab } from '~/data/tab'
import * as customerModule from '~/store/customer'
import * as jacketModule from '~/store/jacket'

export const state = () => ({
    currentTab: new Tab('customer', 'Customer') as Tab,
    tabs: [
        new Tab('customer', 'Customer'),
        new Tab('jacket-style', 'Jacket Style'),
    ] as Tab[],
})

type RootState = ReturnType<typeof state>

export const getters = {
}

export const mutations = mutationTree(state, {
    setCurrentTab(state: RootState, tab: Tab) {
        state.currentTab = tab
    },
})

export const actions = actionTree(
    { state, getters, mutations },
    {
    }
)

export const accessorType = getAccessorType({
    state,
    getters,
    mutations,
    actions,
    modules: {
        customerModule,
        jacketModule
    },
})

the module

import { getterTree, mutationTree, actionTree } from 'typed-vuex'
import { Jacket, defaultJacket } from '~/data/jacket/jacket'
import { EconomicJacket } from '~/data/jacket/economicJacket'

export const state = () => ({
    jackets: [] as Jacket[],
    currentJacket: defaultJacket as Jacket
})

export const getters = getterTree(state, {
})

export const mutations = mutationTree(state, {
    setJackets(state, jackets: Jacket[]) {
        state.jackets = jackets
    },
    setCurrentJacket(state, jacket: Jacket) {
        state.currentJacket = jacket
    }
})

export const actions = actionTree(
    { state, getters, mutations },
    {
        async loadJackets ({ commit }) {
            const jackets: Array<EconomicJacket> = await this.$axios.$get('/json/economic/jackets.json', { responseType: 'json' })
            commit('setJackets', jackets.map(j => EconomicJacket.clone(j)))
        }
    }
)

and I call it running the following code:

this.$store.dispatch('loadJackets')

and to get the data I do:

this.$accessor.jacketModule.jackets

but I am getting

TypeError: Cannot read property 'jackets' of undefined

This is how you might do it for SSR support:

const state = {
  _currentJacket: { size: 10 },
}
const getters = {
  currentJacket(state) => {
    return {
      ...state._currentJacket,
      getSize() {
        return `size is: ${this.size}`
      }
    }
  }
}

If you are using dispatch you should call with the namespace: this.$store.dispatch('jacket/loadJackets').

And instead of jacketModule or customerModule you should use the actual namespace of the module, which is jacket or customer. Docs here have this important line:

The key (submodule) needs to match the Nuxt namespace (e.g. ~/store/submodule.ts)

@danielroe thank you so much, I will think about it =)
I will read more about SSR so I don't struggle a lot handling projects with it haha

Thanks again, I really appreciate your attention.