Code Monkey home page Code Monkey logo

direct-vuex's People

Contributors

304notmodified avatar 5eraph avatar davudsafarli avatar omgimalexis avatar sapphi-red avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

direct-vuex's Issues

Any way to make "export default { } as const" typed?

It seems that defining our modules is done like this:

export default {
    namespaced: true,
    state: { },
    mutations: { },
    // etc
} as const;

Do you know if there's a way to make this of type ModuleOptions?

I tried it like this, but that seems to mess with typescript/intellisense:

const module: ModuleOptions = {
    //...
};
export default module;

(What an amazing library btw! I'm trying to migrate our current code base to this and so far I'm very happy.)

[0.10.4][Q] Working in the UI but failing for Jest

Please, help me to understand why this is failing. I started to use this awesome module, however, I'm facing some troubles with Jest.

Here is my module index.ts (mainly the same but I cannot paste code here due to NDA):

// store/modules/my-module/index.ts
...
export const getDefaultState = (): CoreState => {
  return {
    ...
  }
}

// Deep clone the default state to the store state object:
const state = (): CoreState => getDefaultState()

const myModule = {
  namespaced: true as true,
  state,
  getters,
  actions,
  mutations,
}

export const myModuleActionContext = (context: any) => moduleActionContext(context, myModule)
export const myModuleGetterContext = (args: [any, any, any, any]) => moduleGetterContext(args, myModule)
export default myModule

And here is how I create my (big) store:

...
import myModule from '@store/modules/my-module'
...
import { createDirectStore } from 'direct-vuex'

...

const {
  store,
  rootActionContext,
  moduleActionContext,
  moduleGetterContext,
} = createDirectStore({
  state,
  actions,
  getters,
  mutations,
  modules: {
    ...
    myModule,
    ...
  },
  strict: true,
})
const original = store.original
// Export the classic Vuex store.
export default original

const direct = store
export {
  // The following exports will be used to enable types in the
  // implementation of actions.
  rootActionContext,
  moduleActionContext,
  moduleGetterContext,
  // Export the direct-store
  direct,
}

// The following lines enable types in the injected store '$store'.
export type AppStore = typeof store
declare module 'vuex' {
  interface Store<S> {
    direct: AppStore
  }
}

And then, my test file:

import { direct as store } from '@store'
describe('My module store', () => {
  ...
  const myModuleState = store.state.myModule
  const rootState = store.state
  const vueState = {
    state: myModuleState,
    store,
    rootState,
    rootGetters: store.getters,
    mockedActions: actions,
  }

  // Test mutations
  test('Store mutation: X', () => {
   ...
  })
  ...

The error is about this:

TypeError: Cannot read property 'getters' of undefined

rootGetterContext expects different arguments than provided by the getter

Following the example in the Readme I create a getter in my root store

getters: {
  myGetter(...args) {
    const {state, getters} = rootGetterContext(args);  
  }
}

I am getting the following type error

Argument of type '[RootState, any, any, any]' is not assignable to parameter of type '[any, any]'.
  Types of property 'length' are incompatible.
    Type '4' is not assignable to type '2'.ts(2345)

It seems to be expecting

(method) myGetter(state: RootState, getters: any, rootState: any, rootGetters: any): void

Module "type not assignable" error

Hey,

Just read your Medium article and I'm looking forward to using this package! Unfortunately I'm running into an issue creating the direct store.

Below is the main index.ts for my store: store/index.ts

import Vue from 'vue';
import Vuex, { StoreOptions } from 'vuex';
import { createDirectStore } from 'direct-vuex';
import { RootState } from './types';
import profile from './profile';

Vue.use(Vuex);

const { store } = createDirectStore({
  state: {},
  modules: {
    profile,
  },
});

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

And here is a module that I am trying to include in the store: store/profile/index.ts

import { Module } from 'vuex';
import getters from './getters';
import actions from './actions';
import mutations from './mutations';
import { ProfileState } from './types';
import { RootState } from '../types';


const state: ProfileState = {
  user: undefined,
  authErrorMessage: '',
  authLoading: false,
};

const profile: Module<ProfileState, RootState> = {
  namespaced: true as true,
  state: {
    user: undefined,
    authErrorMessage: '',
    authLoading: false,
  },
  getters,
  actions,
  mutations,
};

export default profile;

The modules property in store/index.ts (rather the profile property) is causing a type assignability error.

Type '{ profile: Module<ProfileState, RootState>; }' is not assignable to type '{ profile: Module<ProfileState, RootState>; } & ModulesImpl<any>'.
  Type '{ profile: Module<ProfileState, RootState>; }' is not assignable to type 'ModulesImpl<any>'.
    Property 'profile' is incompatible with index signature.
      Type 'Module<ProfileState, RootState>' is not assignable to type 'ModuleOptions<any>'.
        Types of property 'actions' are incompatible.
          Type 'ActionTree<ProfileState, RootState> | undefined' is not assignable to type 'ActionsImpl | undefined'.
            Type 'ActionTree<ProfileState, RootState>' is not assignable to type 'ActionsImpl'.
              Index signatures are incompatible.
                Type 'Action<ProfileState, RootState>' is not assignable to type 'ActionImpl'.
                  Type 'ActionObject<ProfileState, RootState>' is not assignable to type 'ActionImpl'.
                    Type 'ActionObject<ProfileState, RootState>' provides no match for the signature '(context: ActionContext<any, any>, payload: any): any'.ts(2322)
index.ts(11, 3): The expected type comes from property 'modules' which is declared here on type '{ state: {}; modules: { profile: Module<ProfileState, RootState>; }; } & StoreOptions<{}>'

Any help would be great! I am happy to provide more details.

Thanks,

Is there a way to get direct-vuex to work with vuex-module-decorators?

I'm using vuex-module-decorators (quick intro here) to write my Vuex modules. This allows me to write class-style modules that are really nice to read and write. However, it is a bit more annoying to get a typed state though.

Is there a way to use direct-vuex together with vuex-module-decorators? I'm currently getting problems when I try to create the direct store with my VuexModule from vuex-module-decorators:

// mymodule.ts
@Module({
  name: "MyModule",
  namespaced: true as true
})
export default class MyModule extends VuexModule {
  // ...
}

// store.ts
const { store, rootActionContext, moduleActionContext } = createDirectStore({
  modules: {
    MyModule
  }
});

IE arrow functions

Hello,
I have been using this amazing package for awhile. But I am struggling with IE 11 support. After compilation arrow functions => are left in code.

I managed to fix this by using ems version of files instead of umd. If i swap "module" and "main" in your package.json the compilation works perfectly and it runs on IE without any problems.

image

I don't really understand how things like EMS and UMD work, but swapping those values fixed my issue, but I don't think its the correct way to fix it :D

Thanks for any suggestions and for your work on this amazing package, HamAndRock.

How to test rootDispatch?

This is my action:

async foo(context: any): Promise<void> {
    const { rootDispatch } = typedContext(context)
    rootDispatch.uiModule.notification("test")
  }

And this is my test:

test.only("foo", async () => {
    const dispatch = jest.fn()
    await actions.foo({ dispatch })

    expect(dispatch).toHaveBeenCalledWith("uiModule/notification", "test")
  })

Expected: test to pass
Actual: Number of calls: 0

What am I doing wrong here?
Thanks!

[Feature] Swap command and module order?

Currently to access a module action you can do: this.$store.direct.dispatch.users.someAction()

Although I am wondering if it would make more sense (and be possible) to swap the order of the command (dispatch) and the module (users), like so: this.$store.direct.users.dispatch.someAction()

The benefit of the latter is that it makes it possible to return the module as a calculated property in your component to reduce bloat:

export default class UserManagement extends Vue {
  private get users() {
    return this.$store.direct.users;
  }

  public someAction() {
    this.users.dispatch.someAction();
  }
}

Or even better in the template:

<h2 class="title">{{users.getters.name}}</h2>

Just an fyi I'm now doing this to achieve the same thing:

  private get users() {
    return {
      state: this.$store.direct.state.users,
      getters: this.$store.direct.getters.users,
      commit: this.$store.direct.commit.users,
      dispatch: this.$store.direct.dispatch.users,
    }
  }

No access to namespaced modules

For some reason I do not have access to namespaced modules 😕

// actions.ts
import { State } from './types';
import { actions } from './actions';
import { createModule } from 'direct-vuex';
import { getters } from './getters';
import { moduleActionContext } from './.store';
import { mutations } from './mutations';

const state: State = {
  bar: ""
}

const fooModuleConfig = {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}

export const fooModule = createModule(fooModuleConfig)
export const getTypedContext = (context: any) =>
  moduleActionContext(context, fooModule)


// module.ts
import { State } from './types';
import { actions } from './actions';
import { createModule } from 'direct-vuex';
import { getters } from './getters';
import { moduleActionContext } from './.store';
import { mutations } from './mutations';

const state: State = {
  bar: ""
}

const fooModuleConfig = {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}

export const fooModule = createModule(fooModuleConfig)
export const getTypedContext = (context: any) =>
  moduleActionContext(context, fooModule)


// store.ts
import Vue from 'vue';
import Vuex from 'vuex';
import { createDirectStore } from 'direct-vuex';
import { fooModule } from './module';

Vue.use(Vuex)

const fooStoreConfig = {
  state: {},
  modules: {
    fooModule
  },
  getters: {},
  actions: {},
  mutations: {}
}

export const {
  moduleActionContext,
  store,
  rootActionContext
} = createDirectStore(fooStoreConfig)


// here I try to access module
store.dispatch.fooModule.foo() // intellisense complains
store.dispatch.foo() // no complaints

Expected: That intellisense would let me access store.dispatch.fooModule.foo()

Actual: It will only let me access store.dispatch.foo()

What am I doing wrong this time? 😄

Uncaught Error: [vuex] must call Vue.use(Vuex) before creating a store instance.

I'm getting the error:

Uncaught Error: [vuex] must call Vue.use(Vuex) before creating a store instance.

Here's my thoughts:
Perhaps direct-vuex has its own VueX installation?

Here's what fixes my problem:

myproject/src/main.ts

import Vue from 'vue'

const { store, rootActionContext, moduleActionContext } = createDirectStore(
  (vuex: any) => Vue.use(vuex), 
  {
      ...
  }
)

direct-vuex.esm.js

import Vuex from 'vuex'

function createDirectStore(callback, options) {
    callback(Vuex)

Here's my code:

myproject/src/main.ts

import Vue from 'vue'
import { createDirectStore } from 'direct-vuex'
import vuex from 'vuex'
import tabs from '@/store/modules/tabs'
import favourites from '@/store/modules/favourites'
import VuexPersistence from 'vuex-persist'

Vue.use(vuex)

const vuexLocal = new VuexPersistence<any>({
  storage: window.localStorage,
  reducer: state => ({ favourites: state.favourites }),
})

const { store, rootActionContext, moduleActionContext } = createDirectStore({
  modules: {
    tabs,
    favourites,
  },
  plugins: [vuexLocal.plugin],
} as any)

Need more complete examples

I am trying to implement direct-vuex in a 'boilerplate' I frequently use when setting up new projects. It is written in typescript with vuex-typex, but direct-vuex is a lot cleaner and closer to the vuex docs, so I would like to switch to it.

However, the docs here are unclear in a couple of areas, specifically with regards to actions. These are the last hurdles I have to clear in the transition.

The first issue:

Actions can't be declared with the object alternative syntax.

I am not sure what this means - I don't fully know what "alternative" means here. Could you give an example of bad/good syntax?

Also, I cannot figure out how to properly use context in vuex (actions, etc.) A sample for writing/using a vuex module would be helpful. For example, from the vuex docs:

const moduleA = {
  // ...
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}

Note that the first argument of the action is the context which in this example contains the module state, module commit, and root state. Likewise, in the root store, the context would have similar properties.

It seems this context argument is still required, but I can't seem to get it to work. Can you please provide an example of how to get type hinting/autocompletion on the context-specific state/commit/etc. via direct-vuex?

If direct-vuex can't get that type information, the argument is still required in actions, but is no longer useful, so it should be communicated that you can use store.state and store.commit instead.

[0.10.4][Limitation] Mutation names not being validated

So, more things around this library (which looks powerful!):

  async someAction (context, { someParameter, someOtherParam = true }: { someParameter: any, someOtherParam?: boolean }) {
    const { commit, rootDispatch } = myModuleActionContext(context)
    commit.fff()
   ...

the line commit.fff() should not work, at compile time, at lint time or any other place but fff is not a valid mutation.

On the other hand, commit. properly autocompletes available mutations and hovering over one of them properly shows the types. So commit.validMutation() properly shows me the types.

What I need is to catch when I'm using commit.fff() because fff doesn't exist.

This is also happening with other use cases, but this clearly shows the problem.

Regards dude!

Q: How to migrate from dynamic vuex modules to dynamic direct-vuex modules?

I read your article, and don't understand how to migrate properly.
I am using vue-property-decorator & register component store as module on beforeCreate hook:

beforeCreate(): void {
	// only if not initialized yet
	const store = this.$store;
	if (!(store && store.state && store.state[storeName])) {
		store.registerModule(storeName, storeConf);
	}
}

where storeConf looks like this:

import { IState } from './state.i';

const defaultState: IState = {
	iamSelect: {
		value: null,
		data: {
			options: [],
		},
	},
};
...

export const storeConf = {
	namespaced: true as true, // direct-vuex needs
	actions,
	getters,
	mutations,
	state: defaultState,
} as const;

I register Store (without modules) only in app file (not migrated totally yet):
const store = createDirectStore(storeConf).store.original;

Does direct-vuex support dynamic registration of modules?

IE 11 Expected ':'

Hi,
i have problem with running the app in IE 11. The problem i in function createDirectStore(options), where it exptect original: original, insead of simple original in const store { ... }

function createDirectStore(options) {
const original = new vuex__WEBPACK_IMPORTED_MODULE_0__["default"].Store(options);
const store = {
    get state() {
        return original.state;
    },
    getters: toDirectGetters(options, original.getters),
    commit: toDirectCommit(options, original.commit),
    dispatch: toDirectDispatch(options, original.dispatch),
    original
};
original.direct = store;
return {
    store,
    rootGetterContext: ([state, getters]) => getModuleGetterContext([state, getters, state, getters], options, options),
    moduleGetterContext: (args, moduleOptions) => getModuleGetterContext(args, moduleOptions, options),
    rootActionContext: (originalContext) => getModuleActionContext(originalContext, options, options),
    moduleActionContext: (originalContext, moduleOptions) => getModuleActionContext(originalContext, moduleOptions, options),
};

}

Is there a way to fix this? Thanks

The type 'readonly [Plugin<RootState>]' is 'readonly' and cannot be assigned to the mutable type 'PluginImpl[]

Type error when using createDirectStore with VuexPersistance plugin:

The type 'readonly [Plugin]' is 'readonly' and cannot be assigned to the mutable type 'PluginImpl[]

Is there a reason PluginImp[] is mutable?


const vuexPersistance = new VuexPersistence<RootState>({
  storage: localStorage,
});

export const {
  store,
  rootActionContext,
  moduleActionContext,
} = createDirectStore({
  modules: {
    // filter,
  },
  strict: process.env.DEV ? true : false,
  plugins: [vuexPersistance.plugin],
} as const);

Q: Is there a way to avoid circular dependency between the root store and modules?

// store/demoModule/index.ts

import actions from "./actions";
import mutations from "./mutations";
import getters from "./getters";
import { createModule } from "direct-vuex";
import { moduleActionContext } from "@/store";

export interface Todo {
  title: string;
  id: number;
}

export interface demoModuleState {
  list: Todo[];
}

const demoModule = createModule({
  state: (): demoModuleState => {
    return {
      list: [
        {
          id: 1,
          title: "Hello world"
        }
      ]
    };
  },
  getters: getters,
  mutations: mutations,
  actions: actions,
  namespaced: true as true
});

export default demoModule;

export const demoModuleActionContext = (context: any) =>
  moduleActionContext(context, demoModule);
// store/index.ts
import Vue from "vue";
import Vuex from "vuex";
import demoModule from "./demoModule";

import { createDirectStore } from "direct-vuex";
Vue.use(Vuex);
const { store, rootActionContext, moduleActionContext } = createDirectStore({
  modules: {
    demoModule: demoModule
  }
});

export default store;

export { rootActionContext, moduleActionContext };

export type AppStore = typeof store;

declare module "vuex" {
  interface Store<S> {
    direct: AppStore;
  }
}
depcruise --validate .dependency-cruiser.js src

  warn no-circular: src/store/index.ts → 
      src/store/demoModule/index.ts →
      src/store/index.ts
  warn no-circular: src/store/demoModule/mutations.ts → 
      src/store/demoModule/index.ts →
      src/store/demoModule/mutations.ts
  warn no-circular: src/store/demoModule/mutations.ts → 
      src/store/demoModule/index.ts →
      src/store/demoModule/mutations.ts
  warn no-circular: src/store/demoModule/index.ts → 
      src/store/index.ts →
      src/store/demoModule/index.ts
  warn no-circular: src/store/demoModule/index.ts → 
      src/store/demoModule/mutations.ts →
      src/store/demoModule/index.ts
  warn no-circular: src/store/demoModule/index.ts → 
      src/store/demoModule/getters.ts →
      src/store/demoModule/index.ts
  warn no-circular: src/store/demoModule/index.ts → 
      src/store/demoModule/actions.ts →
      src/store/demoModule/index.ts
  warn no-circular: src/store/demoModule/getters.ts → 
      src/store/demoModule/index.ts →
      src/store/demoModule/getters.ts
  warn no-circular: src/store/demoModule/actions.ts → 
      src/store/demoModule/index.ts →
      src/store/demoModule/actions.ts

✖ 9 dependency violations (0 errors, 9 warnings). 21 modules, 33 dependencies cruised.

How to mock context object?

Hi,

I would like to test an action similar to this:

async myAction(context: any,): Promise<void> {
    const { commit, rootDispatch, getters } = typedContext(context)

    if (!getters.someProperty) {
        rootDispatch.uiModule.notification("My message")
    }

    commit.DO_SOMETHING()
}

I am struggling on how to mock the context object in my test that I need to pass to my action.

test("myAction", async () => {
    const context = {
      ...store,
      commit: jest.fn()
    }

    await actions.myAction(context)

    expect(context.commit).toHaveBeenCalledWith("DO_SOMETHING")
  })

I have tried the above. But the getters returned from typedContext is an empty object.

What is the easiest way to mock to context object? :)

Q: How to setup @vue/test-utils?

I changed my code to use direct-vuex in actions, browser seems to work without errors, but when I run jest tests, test suite failed to run, cause of typing when I mount component with store

src/unique.blocks/SomeForm/SomeForm.spec.ts:30:19 - error TS2769: No overload matches this call.
      Overload 1 of 3, '(component: VueClass<SomeForm>, options?: ThisTypedMountOptions<SomeForm> | undefined): Wrapper<SomeForm>', gave the following error.
        Argument of type '{ store: { readonly state: { modSomeForm: { iamSelect: { value: IOption | null; data: IData; }; }; }; getters: { modSomeForm: { readonly someValue: IOption | null;
readonly someValues: IOption[]; }; }; commit: { ...; }; dispatch: { ...; }; original: { ...; }; }; localVue: VueConstructor<...>; }' is not assignable to parameter of type 'ThisTypedMountOptions<SomeForm>'.
          Type '{ store: { readonly state: { modSomeForm: { iamSelect: { value: IOption | null; data: IData; }; }; }; getters: { modSomeForm: { readonly someValue: IOption | null; readonly someValues: IOption[]; }; }; commit: { ...; }; dispatch: { ...; }; original: { ...; }; }; localVue: VueConstructor<...>; }' is not assignable to type 'MountOptions<SomeForm>'.
            Types of property 'store' are incompatible.
              Type '{ readonly state: { modSomeForm: { iamSelect: { value: IOption | null; data: IData; }; }; }; getters: { modSomeForm: { readonly someValue: IOption | null; readonly someValues: IOption[]; }; }; commit: { ...; }; dispatch: { ...; }; original: { ...; }; }' is missing the following properties from type 'Store<any>': replaceState, subscribe, subscribeAction, watch, and 3 more.
      Overload 2 of 3, '(component: ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<...>>, options?: ThisTypedMountOptions<...> | undefined): Wrapper<...>', gave the following error.
        Value of type 'typeof SomeForm' has no properties in common with type 'ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<...>>'. Did you mean to call it?
      Overload 3 of 3, '(component: FunctionalComponentOptions<Record<string, any>, PropsDefinition<Record<string, any>>>, options?: MountOptions<Vue> | undefined): Wrapper<...>', gave the following error.
        Argument of type 'typeof SomeForm' is not assignable to parameter of type 'FunctionalComponentOptions<Record<string, any>, PropsDefinition<Record<string, any>>>'.
          Property 'functional' is missing in type 'typeof SomeForm' but required in type 'FunctionalComponentOptions<Record<string, any>, PropsDefinition<Record<string, any>>>'.

    30          const wrapper = mount(SomeForm, { store, localVue });
                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      src/unique.blocks/SomeForm/SomeForm.spec.ts:30:25
        30      const wrapper = mount(SomeForm, { store, localVue });
                                      ~~~~~~~~
        Did you mean to use 'new' with this expression?
      node_modules/vue/types/options.d.ts:132:3
        132   functional: boolean;
              ~~~~~~~~~~
        'functional' is declared here.

What am I doing wrong?

Namespaced related wording in documentation

First, than you very much for making vuex finally usable with TypeScript! 👍

Reading this

Direct-vuex provides several useful helpers for implementation of the store. They are all optional. However, if you want to keep your classic implementation of a Vuex Store, then direct-vuex needs to infer the literal type of the namespaced property. You can write namespaced: true as true where there is a namespaced property. But you don't need to worry about that if you use defineModule.

it seems to me that I don't have to write namespaced: true as true to a module definition when I'm using defineModule helper function. But that does not seem to be true.

When namespaced attribute is omitted everything is registered in global state as expected from the documentation https://vuex.vuejs.org/guide/modules.html#namespacing .

Is that just unfortunate wording or am I doing something wrong and using defineModule should create namespaced module automatically?

#17

[Feature] Stronger typed modules?

Vuex has a very useful generic StoreOptions type to define stores:

const module: StoreOptions<UserState> = {
    state: {
        name: "Harry Potter"
    },
    getters: {
        name: (state) => state.name // Here state automatically has the correct type UserState
    }
}

This is how the same store should be defined with direct-vuex. On larger stores this adds quite some bloat:

export default {
    state: {
        name: "Harry Potter"
    } as UserStore,
    getters: {
        name: (state: UserStore) => state.name
    }
}

Unless I'm mistaken there is no way to use this (or any other) type to achieve the same thing, because of the const assertion. Do you know if there's another way to make store creation stronger typed?

Create multiple instances of vuex

Hi Paleo,

Thanks for this great library and the great docs!

I'm using Jest for unit testing and I'm realizing that the way direct-vuex is recommended in README.md may create issues during parallelized tests.

When setting a sample app like this:

// src/store/index.ts

const {
  store,
  rootActionContext,
  moduleActionContext,
  rootGetterContext,
  moduleGetterContext
} = createDirectStore({
  // … store implementation here …
})

// Export the direct-store instead of the classic Vuex store.
export default store

// The following exports will be used to enable types in the
// implementation of actions and getters.
export {
  rootActionContext,
  moduleActionContext,
  rootGetterContext,
  moduleGetterContext
}
// src/main.ts

import Vue from "vue"
import store from "./store"

new Vue({
  store: store.original, // Inject the classic Vuex store.
  // …
}).$mount("#app")
// tests/unit/MyComponentUsingAStore.spec.ts

import { shallowMount } from '@vue/test-utils'
import MyComponentUsingAStore from '@/components/MyComponentUsingAStore.vue'

describe('MyComponentUsingAStore.vue', () => {
  it('Test 1', () => {
    const wrapper = shallowMount(MyComponentUsingAStore)
    expect(wrapper.find('.loading').text()).toEqual("Loading...")
  })

  it('Test 2', () => {
    const wrapper = shallowMount(MyComponentUsingAStore)
    expect(wrapper.find('.loading').text()).toEqual("Loading...")
  })
})

Jest is running its tests in parallel.
So, this scenario can generates errors :

  • [Test 1] When mounting MyComponentUsingAStore's component, the created hook of this component calls an async action, which will mutate the store in 1ms.
  • [Test 2] When mounting MyComponentUsingAStore's component, the vuex's state which has been populated in [Test 1] is used. So, the state fails.

As a workaround to this, we can :

However, it's far from an ideal solution as we lose test parallelization.

A solution would be export a createStore function instead of the store instance directly (as vuex documentation does). It would allows the user to create a new instance of vuex / direct-vuex for each test.
However, this do not seems possible because of the circular dependency between the store and the modules.

Is there another way to avoid sharing an instance of vuex ?

Thanks again :)

type safety for the ActionContext

Thanks for the great library.
I like how simple the idea is most importantly, do not need to change much code from the traditional vuex approach.

The problem

However, it would be awesome if we can use strongly type for the ActionContext.
For example,

actions: {
  fetchSomething({state, rootState}, payload: SomePayload) {  
  }
}

Expected behavior

state and rootState should be of type ModuleState and RootState, instead of any.

Any way to get a type for context to make eslint happy?

We recently migrated our project from tslint to eslint with typescript support.

We now get a no-explicit-any warning for our context parameters, e.g.:

async requeueContracts(context: any) { // @typescript-eslint/no-explicit-any: Unexpected any. Specify a different type.
    const { state, getters, commit, dispatch } = actionContext(context);
    await myClient.requeue();
},

Do you know if it's possible to make this context typed? I would rather not ignore this eslint warning.

store only shows the state, but no dispatch or commits

I love your approach of making the store typescript friendly. I am looking for a while how to do this.
If I make a store with 2 modules in, I can use the state directly, but the commits and dispatches are always empty. Is this expected for now?

How to test an action?

Hi,

Thanks for writing this great library. I can't believe this is not built into vuex. I hope vuex + typescript will be a better match once vue3 is out.

Anyway, my question is: how can I test an action now that we use moduleActionContext inside the actions?

I get:

TypeError: Invalid value used as weak map key

export const fooActionContext = (context: any) =>
    moduleActionContext(context, fooModule)

My code is:

// foo.ts
import { Context } from './types';
import { fooActionContext } from './store';

export const actions = {
  foo(context: Context) {
    const { commit } = fooActionContext(context)
  }
} as const



// fooModule.ts
import { actions } from './foo';

export const fooModule = {
  namespaced: true,
  state: {},
  getters: {},
  actions,
  mutations: {}
} as const



// foo.test.ts
import { actions } from './foo';

test("foo", async () => {
  const commit = jest.fn()
  await actions.foo({ commit } as any)

  expect(commit).toHaveBeenCalled()
})


// store.ts
import Vue from 'vue';
import Vuex from 'vuex';
import { createDirectStore } from 'direct-vuex';
import { fooModule } from './fooModule';

Vue.use(Vuex)

export const { moduleActionContext } = createDirectStore({
  state: {},
  modules: {
    fooModule
  }
} as const)

export const fooActionContext = (context: any) =>
  moduleActionContext(context, fooModule)

What am I doing wrong? And how should I test an action how that we call fooActionContext() inside the action?

Thanks in advance!

Q: declare module "vuex" for this.$store

How to check this code is working?

readme.md:

export type AppStore = typeof store
declare module "vuex" {
  interface Store<S> {
    direct: AppStore
  }
}

Is it means that I can use some code instead of (working)

import { store as vueAppStore } from '~/VueApp/store';
...
vueAppStore.dispatch.modSomeForm.getSomeValues

using this?
this.$store.dispatch.modSomeForm.getSomeValues

When I typing in VSCode this.$store.dispatch there is nothing about getSomeValues =(

Property 'alert' does not exist on type 'never'.

Hello,
I think that your product is super. But I have some problems with it and I was not able to google it.
When I typing the project, everything looks fine. But when I try to build it, i got error:

Property 'alert' does not exist on type 'never'.

Instead of 'alert' you can put any of my vuex store module. In development build it is compiled with this error, but everything works fine.

const SUCCESS = 'success';
const WARNING = 'warning';
const ERROR = 'error';
const CLEAR = 'clear';

const state: AlertStore = {
    type: null,
    message: null
}
const mutations = {
    [SUCCESS](state: AlertStore, message: string) {
        state.type = AlertType.Success;
        state.message = message;
    },
    [WARNING](state: AlertStore, message: string) {
        state.type = AlertType.Warning;
        state.message = message;
    },
    [ERROR](state: AlertStore, message: string) {
        state.type = AlertType.Error;
        state.message = message;
    },
    [CLEAR](state: AlertStore) {
        state.type = null;
        state.message = null;
    },
};

const actions = {
    success({ commit }: any, message: string) {
        commit(SUCCESS, message);
        },
    warning({ commit }: any, message: string) {
        commit(WARNING, message);
    },
    error({ commit }: any, message: string) {
        commit(ERROR, message);
    },
    clear({ commit }: any) {
        commit(CLEAR);
    },
};

const getters = {};

    export interface AlertStore {
    type: AlertType | null;
    message: string | null;
}

export enum AlertType {
    Success = 'success',
    Warning = 'warning',
    Error = 'error',
}

export default {
    namespaced: true as true,
    state: state as AlertStore,
    getters,
    actions,
    mutations
};

Calling store.dispatch.alert.clear() show error, that alert does not exist on type 'never'. Pointing to 'dispatch'. The same with 'getters'.

Optional payloads not detected

I have an action where I want it to be called with either a payload or nothing.

actions: {
  async myAction(context, payload?: PayloadType) {
    // ...
  }
}
store.dispatch.myAction();
               ^---------- Expected 1 arguments, but got 0.ts(2554)
                           direct-types.d.ts(97, 7): An argument for 'payload' was not provided.

I also tried async myAction(context, payload: PayloadType | undefined) but got the same error.

For now I've worked around the issue by passing in null but it would be nice if it were possible to make the parameter optional.

Mistake in documentation? Variable used before it's assigned. + What's the work around?

Hey. In the module sample on how to use moduleActionContext, mod1ActionContext is used before it's assigned.

actions: {
    loadP1(context, payload: { id: string }) {
      const { dispatch, commit, getters, state } = mod1ActionContext(context)
      // Here, 'dispatch', 'commit', 'getters' and 'state' are typed.
    }
  },
})

export default mod1
export const mod1ActionContext = (context: any) => moduleActionContext(context, mod1)

Problem is, moduleActionContext requires the module to be created, so you can't create it before either.

My workaround has been to create the module in 2 steps, first creating the module with state & mutations then assigning actions, which is not elegant:

const Module = createModule({
  state: (): ModuleState => { // whatever } ,
  getters: { // whatever },
  mutations: { // whatever },
`  actions: {} 
});

export const ModuleActionContext = (context: any) => moduleActionContext(context, Module);

Module.actions = {
  whatever(context: any, payload: { foo: boolean }) {
    const { commit } = ModuleActionContext(context);
    commit.THE_MUTATION(payload.foo);
  }
};

Am I seeing something wrong?

Missing module when calling getters, mutations, actions

Hi, Paleo!
I have followed your example but when calling getters, actions, mutations, the following problem occurs:

State: OK
store.state.mod1.p1 ---> OK

Getters: missing module
store.getters.p1OrDefault -----> call OK

But, I want it to be like this:
store.getters.mod1.p1OrDefault

How I can do?

Changelog or Releases

I don't see a changelog or any tagged releases. It's a bit hard to keep track of what has been updated, have to go back and look at each commit since last known download. Can you add a changelog, or at the very least tag things to correspond with npm releases?

TypeError: Cannot read property 'Store' of undefined

v 0.9.9,

Chrome 79.0.3945.79
Firefox 72.0.2
Opera 66.0.3515.44
First page loading - error, second - ok.

When I use SystemJS with direct-vuex.umd.js version & vuex.min.js I have an error:

Uncaught (in promise) TypeError: Cannot read property 'Store' of undefined
    at createDirectStore (direct-vuex.umd.js:17)

It is about
Vuex = Vuex && Vuex.hasOwnProperty('default') ? Vuex['default'] : Vuex;

Tried to use console:
Vuex // is ok - object as expected
Vuex = Vuex && Vuex.hasOwnProperty('default') ? Vuex['default'] : Vuex; is equal Vuex

May be I have that error because of vuex.min.js is loaded after direct-vuex.umd.js?

Q: Declaring state

About declaring state: is it correct to declare state like this?

interface IState {
	iamSelect: {
		value: IamSelectOption | null;
		data: IamSelectData;
	};
}

const defaultState: IState = {
	iamSelect: {
		value: null,
		data: {
			options: [],
		},
	},
};

Please bundle module/browser version (like vuex.min.js format)

Can't use in browser, cause of error:

Uncaught ReferenceError: exports is not defined
    at direct-vuex.js:5

I am using SystemJs for production (with ie11 support) bundle with type="systemjs-importmap" like this:

{
	"imports": {
		"direct-vuex": "https://cdn.jsdelivr.net/npm/[email protected]/dist/direct-vuex.min.js",
		"vue": "https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js",
		"vuex": "https://cdn.jsdelivr.net/npm/[email protected]/dist/vuex.min.js",
		"wretch": "https://cdn.jsdelivr.net/npm/[email protected]/dist/bundle/wretch.min.js"
	}
}

and all ok except direct-vuex =(

IE11 not working with umd

Chrome is ok, but IE11 is not.

If I remove direct-vuex code, IE11 is ok.
If not, I have error:

Unhandled promise rejection TypeError: unsupported method "entries"
stack: 
at gettersFromOptions (https://cdn.jsdelivr.net/npm/[email protected]/dist/direct-vuex.umd.js:68:30)
at toDirectGetters (https://cdn.jsdelivr.net/npm/[email protected]/dist/direct-vuex.umd.js:58:13)
at createDirectStore (https://cdn.jsdelivr.net/npm/[email protected]/dist/direct-vuex.umd.js:18:9)
at e.init (http://localhost:3000/bundle.system.js:1:19479)
...

moduleGetterContext error: "Function implicitly has return type 'any'..."

I have this error when trying to define Getters:

'userGetters' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.ts(7022)

I can access store.getters.canUpdateUsers from my component but the getter is just executed one time and the type of getters is any. This is the exact type of store.getters:

(property) getters: {
    [x: string]: any;
    user: {
        [x: string]: any;
    };
}

This is the getter part of my module:

export const userGetters = defineGetters<UserState>()({
    canUpdateUsers(...args) {
        // const { state } = userGetterContext(args);
        const { state, getters, rootState, rootGetters } = mod1GetterContext(args)
        console.log(state);

        return (
            state.currentUser.role == "admin" ||
            state.currentUser.role == "presenter"
        );
    },
});
const mod1GetterContext = (args: [any, any, any, any]) => moduleGetterContext(args, userModule)

And this is the module itself:

export const userModule = defineModule({
  state: {
    userList: [],
    currentUser: {},
  } as UserState,

  actions: userActions,
  mutations: userMutations,
  getters: userGetters,
});

I already disabled no-use-before-define eslint rule. It seems that it's not related to this error.

Q: Action context & migrate actions realisation from dispatch('smth') to strict type calling actions

For actions I am using destructuring for readable declarations in parameters:

export const getSomeValues = (
	{ dispatch }: { dispatch: Function },
	{ label }: ISomeValue,
): Promise <Array<ISomeValue> | void> => SomeSvc
	.fetchData({ label })
	.then((resp) => dispatch('setSomeValues', resp));

And I have test like this:

describe('vuex SomeForm IamSelect actions', () => {
	it('getPoints after getting data raise appropriate action', async () => {
		expect.assertions(1);

		const dispatch = jest.fn();
		await actions.getSomeValues({ dispatch }, { label: 'Рим' });

		expect(dispatch).toHaveBeenCalledWith('setSomeValues', expect.anything());
	});
});

with stubbing dispatch/commit functions.

I'm searching also another examples of usage direct-vuex, and found some code:

export default createActions({
    fetchCustomersFromDB(context) {
        const { commit } = rootActionContext(context);
        commit.isLoadingCustomerList(true);
        stitchApi
            .getAllCustomers()
            .then(customers => {
                // console.log("Success:", result);
                commit.customers(customers);
                commit.isLoadingCustomerList(false);
            })
            .catch(err => {
                console.error(`Failed: ${err}`);
                commit.isLoadingCustomerList(false);
            });
    },
});

If I declare input parameters in actions through rootActionContext or (for Module) through moduleActionContext (not with destructing), I can't test actions with jest stubs.

How to declare actions with destructed parameters to get code like dispatch.action1 instead of dispatch('action1')?

Does this package support SSR?

First of all thanks for your work on this, it is a great looking wrapper for vuex. I'm starting a new app and trying to incorporate server side rendering and am running into a bit of difficult implementing direct-vuex in the pattern defined at https://ssr.vuejs.org/guide/data.html#data-store & https://ssr.vuejs.org/guide/data.html#store-code-splitting

Maybe my inexperience with your library and SSR I'm not seeing a direct solution that would allow me to have a factory function to generate a new store for both the client/server as well as define modules based on the return value of createDirectStore?

Typing modules added by plugin

Hello I'm using the feathers-vuex Plugin for Vuex. To make it short how it works is that you define multiple plugins for your backend services and these plugins register namespaced modules which can communicate with the feathers backend service.
However since these modules are generated and registered inside a Plugin, I have no Idea how to enable them inside the direct-vuex store and also generate a Typing for them.
I defined my store the following way:

export const {
  store,
  rootActionContext,
  moduleActionContext,
  rootGetterContext,
  moduleGetterContext,
} = createDirectStore({
  strict: debug,
  state: {
    empty: true,
  },
  mutations,
  actions,
  modules: {
    desktop,
    audio,
    connectionStatus,
  },
  plugins: [
    ...servicePlugins,
    auth,
  ],
});

servicePlugins is a list of feathers-vuex plugins, which each register a namespaced module containing getters by using store.registerModule, mutations and actions. My custom defined modules which are defined by using direct-vuex defineModule method are mapped correctly, however the modules defined by the plugins are missing in direct-vuex getters and actions.
Is there a way to add these to direct-feathers to be able to have them types?

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.