Giter Site home page Giter Site logo

victorgarciaesgi / vuex-typed-modules Goto Github PK

View Code? Open in Web Editor NEW
35.0 6.0 8.0 432 KB

🧰 A VueX wrapper to fully type your modules without boilerplate!

TypeScript 100.00%
vuex vuex-modules vuex-wrapper dynamic-modules vuex-typed vuex-store vuex-plugin

vuex-typed-modules's Introduction

🧰 vuex-typed-modules

npm version npm downloads npm downloads

A VueX 3 & 4 wrapper that provides type safe hooks and handlers to your Vuex Store modules with less code!

4.x Support Vue 2 & 3, and Vuex 3 & 4 and added composition-api support (Thanks to vue-demi)

3.x Working with Vue 2, and Vue 3 with Vuex4 (but not hooks)

Installation

npm i vuex-typed-modules
#or
yarn add vuex-typed-modules

Usage for version 4.x

Define Module and hook

import { createVuexModule } from 'vuex-typed-modules';

export const [testModule, useTestModule] = createVuexModule({
  name: 'testModule',
  state: {
    count: 1,
  },
  mutations: {
    addCount(state, number: number) {
      state.count += number;
    },
  },
  actions: {
    async addCountAsync(_, count: number): Promise<void> {
      await myAsyncFunction(count);
      // Calling mutation
      testModule.mutations.addCount(count);
    },
  },
});

Module declaration (For 4.x and 3.x)

In your main.ts

import { Database } from "vuex-typed-modules";
import { testModule } from '~modules'

const database = new Database({ logger: true });
const store = new Vuex.Store({
  plugins: [database.deploy([testModule])];
})

// If you're using Vue 3:
createApp(App).use(store).mount("#app");

// If you're using Vue 2:
new Vue({
  store,
  render: h => h(App)
}).$mount("#app");

For Nuxt.js

// store/index.ts
import { Database } from 'vuex-typed-modules';
import { testModule } from '~modules';

const database = new Database({ logger: process.browser });
export const plugins = [database.deploy([testModule])];

export const state = () => ({});

Usage in your components or in other modules!

<template>
  <div class="hello">
    {{ count }}
    <button @click="increment">increment</button>
  </div>
</template>
import { defineComponent, onBeforeUnmount } from 'vue';
import { testModule } from '~/modules';

export default defineComponent({
  name: 'Home',
  setup() {
    const {
      state: { count },
      actions: { increment },
    } = useChildStoreModule();

    return {
      count,
      increment,
    };
  },
});

Note

Store hooks return refs for the state. It can be overwridden by using the option unwrap

With Refs

// You can destructure state when using refs
const {
  state: { count },
} = useTestModule(); // count is of type `Ref<number>`
console.log(count.value);

Without Refs

// If you destructure the state, it will loses reactivity
const { state } = useTestModule({ unwrap: true });
state.count; // count is of type `number`

Dynamic Modules

For dynamic modules, simply use the class VuexDynamicModule instead

import { createVuexDynamicModule } from 'vuex-typed-modules';

export const dynamicModule = createVuexDynamicModule({
  name: 'dynamicModule',
  logger: false,
  state: {
    count: 1,
    type: null,
  },
  actions: {
    // Due to limitions of Typescript, I can't provide typings for infered mutations and getters inside the same object.
    // It would make an infinite loop (I tried).
    // For dynamic module you can fallback on "commit" "dispatch" and "getters"
    exemple({ state, commit, dispatch, getters }, param: string) {
      // ...
    },
  },
});

Usage

<script lang="ts">
import { defineComponent, onBeforeUnmount } from 'vue';
import { dynamicModule } from '~/modules';

const [ChildStoreModule, useChildStoreModule] = dynamicModule.instance('child-store');
// Dot not declare it in other files, only import it from here

export default defineComponent({
  name: 'TestView',
  setup() {
    ChildStoreModule.register();
    const {
      state: { count },
    } = useChildStoreModule();

    onBeforeUnmount(() => {
      ChildStoreModule.unregister();
    });

    return {
      count,
    };
  },
});
</script>

Default module helpers

Vuex types modules also add helpers functions on top of your module to prevent from writing short mutations

testModule.resetState();
// Reset your module to the initial State
// You can specify only the property you want to update
testModule.updateState({
  count: 3,
});

// You can also give a callback function to have access to the current state
testModule.updateState((state) => ({
  count: state.count + 2,
}));

// And also mutate the state directly (A bit heavier on the update)
testModule.updateState((state) => {
  state.count++;
});

-------------------------------------------

Usage for 3.x

Define Module

Create a test.module.ts in your store folder

import { VuexModule } from 'vuex-typed-modules';

export const testModule = new VuexModule({
  name: 'testModule',
  state: {
    count: 1,
  },
  mutations: {
    addCount(state, number: number) {
      state.count += number;
    },
  },
  actions: {
    async addCountAsync(_, count: number): Promise<void> {
      await myAsyncFunction(count);
      // Calling mutation
      testModule.mutations.addCount(count);
    },
  },
});

Usage in your components or in other modules!

<template>
  <div class="hello">
    {{ count }}
    <button @click="increment">increment</button>
  </div>
</template>
import { Component, Prop, Vue } from 'vue-property-decorator';
import { testModule } from '~/modules';

@Component
export default class Home extends Vue {
  get count() {
    return testModule.getters.count;
  }

  async increment() {
    await testModule.actions.addCountAsync(2);
  }
}

Tests

// user.spec.ts
import { createTestStore } from 'vuex-typed-modules';
import { createLocalVue } from '@vue/test-utils';
// import state, actions, mutations and getters from some store module

const configStore = {
  state,
  actions,
  mutations,
  getters,
};

const localVue = createLocalVue();
localVue.use(Vuex);

const store = createTestStore(configStore);

describe('User Module', () => {
  it('name should be empty', () => {
    expect(store.state.name).toBe('');
  });
  it('name should change to Foo', () => {
    store.dispatch('CHANGE_NAME', 'Foo');
    expect(store.state.name).toBe('Foo');
  });
});

vuex-typed-modules's People

Contributors

dependabot-preview[bot] avatar mplewis avatar siimaoo avatar victorgarciaesgi avatar victortotem 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

vuex-typed-modules's Issues

Add Documentation for Actions

Describe how actions are defined and how an action calls mutations (typed).

I've tried to find out myself and open a Documentation PR, but neither of the found solutions were working as expected.

  1. Use context.commit - no types present for the mutation
  2. Use mutation(context.state) - vuex complains that a mutation is called outside of a commit

calling mutation inside action in the VuexModule

In your example you are self referencing testModule inside the action.

const actions = {
  async addCountAsync(context, count: number): Promise<void> {
    await myAsyncFunction(count);
  // Calling mutation
    testModule.mutations.addCount(count);
  }
},

I think that context parameter should be used here, no?
In a regular store, the context parameter is used for calling the mutation inside the action.
See https://vuex.vuejs.org/guide/actions.html
so, context.commit is used there, but i guess you want to do something that leverages typings...

I adapted the action on the sample I sent you earlier to:

,
  actions: {
    increment (context, count) {
      context.commit('addCount', count)
    }
  }

This works, but of course there is no typing.
Any suggestion from your side?
Cheers
paul.

How to use getters.

This is my code, and the getter isn't working.
No clue what's going wrong.

STORE CONFIG

import Vue from 'vue'
import Vuex from 'vuex'

import { Database } from 'vuex-typed-modules'
import { testModule } from './test.module.js'

Vue.use(Vuex)
const database = new Database({ logger: true })
export default new Vuex.Store({
  plugins: [database.deploy([testModule])]
})

MODUL/E

import { VuexModule } from 'vuex-typed-modules'

export const testModule = new VuexModule({
  name: 'testModule',
  state: {
    count: 1,
    myProp: 'banana'
  },
  getters: {
    testGetter (state) {
      return state.myProp
    }
  },
  mutations: {
    addCount (state, number) {
      state.count += number
    }
  }
})

SCREEN:

<template>
  <q-page padding>
    {{nativeState}}
    <q-btn
      label='inc'
      @click='increment'
    ></q-btn>
    <div>getter: {{ourTestGetter}}</div>
  </q-page>
</template>

<script>
import { testModule } from '../store/test.module'
export default {
  // name: 'PageName',
  methods: {
    increment () {
      testModule.mutations.addCount(5)
    }
  },
  computed: {
    nativeState () {
      return testModule.state.count
    },
    ourTestGetter () {
      return testModule.getters.testGetter
    }
  }
}
</script>

GettersTree definition

Why does GettersTree<S> type have an optional state parameter? I think it should be mandatory.
Ideally it should be compatible with the original Getter signature:
export type Getter<S, R> = (state: S, getters: any, rootState: R, rootGetters: any) => any;
It would be great to have an ability just to pass existed vuex-typed actions/getters/mutations to VuexModule .

Adding a getter in the vuex class, breaks type inference (intellisense)

When you have, a VuexModule without getters, intellisense is working fine (even in js files with the //@ts-check.

// @ts-check
import { VuexModule } from 'vuex-typed-modules'

export const testModule = new VuexModule({
  name: 'testModule',
  state: {
    count: 1,
    myProp: 'banana'
  },
  mutations: {
    addCount (state, number) {
      state.count += number
    }
  },
  actions: {
    increment (context, count) {
      testModule.mutations.addCount(count)
    }
  }
})

As you can see:

image

When you add now a getter, the type inference is completely broken.

Broken helpers methods with Vue3 + Vuex4

I'm using Vue 3 in my project and due to lack of type support in Vuex 4 (hope coming in Vuex 5) I use your package. Works perfectly except for the helper's method. I'm getting this error:

runtime-core.esm-bundler.js?9e79:217 Uncaught TypeError: Cannot read property 'set' of undefined
    at eval (index.js?c7ff:12)
    at Array.map (<anonymous>)
    at Store._mutations.resetState (index.js?c7ff:11)
    at wrappedMutationHandler (vuex.esm-bundler.js?58d9:808)
    at commitIterator (vuex.esm-bundler.js?58d9:438)
    at Array.forEach (<anonymous>)
    at eval (vuex.esm-bundler.js?58d9:437)
    at Store._withCommit (vuex.esm-bundler.js?58d9:596)
    at Store.commit (vuex.esm-bundler.js?58d9:436)
    at Store.boundCommit [as commit] (vuex.esm-bundler.js?58d9:376)

I think it's due to you are using the Vue.set() in your package.

Are you planning to upgrade your package for Vue 3 and Vuex 4 or not?

I use a workaround for resetState() with overriding it and calling my mutation for reset store state.

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.