@vue-polar/statemanager

A statemanager implementation for typescript/vue

Usage no npm install needed!

<script type="module">
  import vuePolarStatemanager from 'https://cdn.skypack.dev/@vue-polar/statemanager';
</script>

README

README

VUE-POLAR / Statemanager

V1.0.2
Typescript required

The "Vue-polar / statemanager" is a replacement for vuex. it keeps all the reactivity that you are used to from vue. But adds a whole typescript written statemanager to the market.

How do I get set up?

Usage with yarn

yarn add @vue-polar/statemanager

Usage with npm

npm -i @vue-polar/statemanager

Dependencies
  • "reflect-metadata": "^0.1.13",
  • "vue": "^2.6.11",
  • "vue-property-decorator": "^8.3.0",
Dev Dependencies
  • "typescript": "~3.7.5",
Typescript Configuration
  • "experimentalDecorators": true,
  • "emitDecoratorMetadata": true,
ESlint Configuration
  • "@typescript-eslint/no-explicit-any": "off",
Usage

Writing a store:

1 . First thing we should do is create a new typescript file. Lets say we call it: CounterStore.ts, Lets also make sure we've got our imports setup.

import State from "@vue-polar/statemanager";
import StateManagerModule from "@vue-polar/statemanager/src/StateManagerModule";

2 . The first piece of code is going to define the actual state properties inside our store. So lets say we want to keep track of a number. We'll need to create an interface that looks like:

interface CounterState {
    count: number;
}

3 . Next thing we want to do is create a few Types to be used as a code completion reference in our Components. Lets say we want to be able to increment the number and retrieve the number.

export type getCounter = () => number;
export type incrementCounter = (amount: number) => boolean;

As you might've noticed we're creating the blueprint for the Getter and Action methods on our store, this is because we're still holding up to the same structure vuex has to make it easier to understand for people just starting with typescript but have worked with vanilla vuex before.

4 . Now we are finally going to create our store. Which will look something like:

@State.Decorators.Module
export default class AuthenticationStore extends StateManagerModule<CounterState>
{
    namespace = "AuthenticationStore";

    //State should always match the created interface in this case: {CounterState}
    protected state = {
        count: 0
    };

    //Getters should only ever return state properties. Not edit them.
    @State.Decorators.Getter
    getCounter(): number
    {
        return this.state.counter;
    }

    //Standard is that you only use mutations to directly edit the stores state.
    @State.Decorators.Mutation
    private addToCounter(add: number): void
    {
        this.state.counter += add;
    }

    //Actions should then call the mutation and might add some extra functionality beyond state manipulation.
    @State.Decorators.Action
    incrementCounter(amount: number)
    {
        this.addToCounter(amount);
    }
}

As you might've noticed we're extending from

StateManagerModule < CounterState >
This is because we're inheriting some functionality and hard defining our state typing.

Note: It is extremely important that you give a correct interface with the Generic parameter

What you've also might've noticed is that we are using decorators to define types of methods. Here we have a full cheatsheet:

@State.Decorators.Module
    //This is used to register the module to our statemanager. 
    //Without this line it will basicly only be a class with some methods.
    //This also makes sure you can retrieve the module from the statemanager:
    //{state-import}.getModule({namespace}); // needed for component decorators

@State.Decorators.Getter
    //This is used to register the method as a Getter.
    //You are able to call this on the module with:
    //{your-module}.get({methodName},{params});
    //state-import}.get({namespace},{methodName},{params});

@State.Decorators.Action
    //This is used to register the method as an Action.
    //You are able to call this on the module with:
    //{your-module}.dispatch({methodName},{params});
    //{state-import}.dispatch({namespace},{methodName},{params});

@State.Decorators.Mutation
    //This is used to register the method as a Mutation
    //Note: These should always be protected or private.

Writing a component:

  1. Lets start by creating a new component. Lets name it CounterComponent:
<template>
   <div id="counter">
       Below you can see the Getters being called: <br>
       {{ getCounter() }} |  {{ getCounterAlias() }}<br><br>

       And on this button you will see 2 Actions being called<br>
       <button @click="incrementCounter(123 ); incrementCounterAlias(111);">Increment Counter!</button>
   </div>
</template>
   
<script lang="ts">
   import { Component, Vue } from "vue-property-decorator";
   

   //Here we import the State
   import State from "@vue-polar/statemanager"
   
   //Here we import the store method types that we created to enable typehinting
   import {getCounter,incrementCounter} from "@/stores/CounterStore";
    
   //Last but not least here we retrieve the store module we created
   const CounterStore = State.getModule('CounterStore');

   @Component
   export default class Counter extends Vue
   {
       //To be able to call the getters in our component we need to link them to our class
       //This decorator will inject the method from {CounterStore} into this property.
       //By default it will match on the methodname from the store and the propertyname in your component
       @CounterStore.Decorators.Getter()
       public getCounter!: getCounter;

       //Ofcourse you might need 2 stores that use the same methodName.
       //Therefore we made it possible to create aliasses. Instead of using the propertyname.
       //We'll simply be giving the methodname to the Decorator as a parameter.
       //This makes sure we can name the property however we like
       @CounterStore.Decorators.Getter('getCounter')
       public getCounterAlias!: getCounter;

       //Same thing goes for the actions that goes with the Getters
       @CounterStore.Decorators.Action()
       public incrementCounter!: incrementCounter;

       //Same thing goes for the actions that goes with the Getters
       @CounterStore.Decorators.Action('incrementCounter')
       public incrementCounterAlias!: incrementCounter;
   }
</script>

Now when wanting to reach the State in a normal typescript class. All you need to do is import the State. Fetch your module use {state-import}.getModule({namespace}) and use either the .get() or the .dispatch() methods.

Contribution guidelines

We are currently still working on this package any suggestions and/or contribution is welcome

Writing tests

Writing tests should be done in JEST and written according TDD

Code review

We will review the code submitted before there is any permanent action taken. Please do not waste my time on anything that is outside of the scope of this package.

Who do I talk to?

stanley-leroy-gerrits