ajwah-vue-store
TypeScript icon, indicating that this package has built-in type declarations

0.1.2 • Public • Published

Ajwah

Rx based store library for React, Vue, Angular, Preact. Manage your application's states, effects, and actions easy way. It's easy to use in functional components with React hooks.

Installation

>> npm install ajwah-store
>> npm install ajwah-devtools

In Ajwah there are two different coding styles

  • Coding by Decorators
  • Coding by Convention

Here are the samples of all the decorators and it's corresponding coding by convention

@Action()

    @Action('Inc')
    increment(state, action){
        return updateObject(state, { count: state.count + 1, msg: '' })
    }
 
    // Convention: function name starts with `action` followed by  action name - [action][actionName](...){...}
 
    actionInc(state, action){
        return updateObject(state, { count: state.count + 1, msg: '' })
    }

@Effect()

    @Effect()
    asyncIncrement(actions:Actions, store:StoreContext){
        return actions.pipe(
            ofType('AsyncInc'),
            debounceTime(500),
            mapTo({type:'Inc'})
        )
    }
 
    // Convention: function name starts with `effect` followed by anything - [effect][any](...){...}
 
    effectAsyncInc(actions:Actions, store:StoreContext){
        return actions.pipe(
            ofType('AsyncInc'),
            debounceTime(500),
            mapTo({type:'Inc'})
        )
    }
    //@Effect(...) decoretor: you may pass `dispatch:flase` -  by default it's true. if you pass `false`, you effect should be disabled.
 
    @Effect({dispatch:flase})
    asyncIncrement(actions:Actions, store:StoreContext){
        return actions.pipe(
            ofType('AsyncInc'),
            debounceTime(500),
            mapTo({type:'Inc'})
        )
    }
 
    //Convention: for `dispatch:false` - just function name ends with `_ndispatch`
 
    effectAsyncInc_ndispatch(actions:Actions, store:StoreContext){
        return actions.pipe(
            ofType('AsyncInc'),
            debounceTime(500),
            mapTo({type:'Inc'})
        )
    }
 
    // you may use `For` for getting rid of `ofType('...')` - [effect][For][actionName](...){...}. 
    // Use 'Or' for multiple actions name. ex: effectForAsyncIncOrDec(...) 
    // - [effect][For][actionName][Or][actionName][Or][actionName][...](){}
    effectForAsyncInc(actions:Actions, store:StoreContext){
        return actions.pipe(
            //ofType('AsyncInc'), now it's not necessary
            debounceTime(500),
            mapTo({type:'Inc'})
        )
    }
    // '_ndispatch' with `For` ex: effectForAsyncInc_ndispatch()
    effectForAsyncInc_ndispatch(actions:Actions, store:StoreContext){
        return actions.pipe(
            debounceTime(500),
            mapTo({type:'Inc'})
        )
    }
 

@State()

    @State({
        name: 'counter',
        initialState: { count: 5, msg: '' }
    })
    class CounterState{
 
    }
 
    //Convention:
 
    class CounterState{
            name= 'counter';
            initialState={ count: 5, msg: '' };
        
    }
 

@EffectKey()

    @EffectKey(DYNAMIC_EFFECTS_KEY)
    class DynamicEffect{
 
    }
 
    //Convention:
 
    class DynamicEffect{
 
       effectKey=DYNAMIC_EFFECTS_KEY;
    }
 

Note: Please remember the starts with 'action' and 'effect'. This is by default. You may change whatever you want into the 'setStoreContext'

    Vue.use(AjwahStore, {
        states: [CounterSate, TodoState],
        effects: [TodoEffects],
        devTools: devTools({ maxAge: 10 }),
        actionsMethodStartsWith: 'myAction', // default 'action'
        effectsMethodStartsWith:'myEffect'  // default 'effect'
    })
    
    //Now your actions and  effects should be
 
    myActionInc(state, action){
        return updateObject(state, { count: state.count + 1, msg: '' })
    }
 
    myEffectAsyncInc(actions:Actions, store:StoreContext){
        return actions.pipe(
            ofType('AsyncInc'),
            debounceTime(500),
            mapTo({type:'Inc'})
        )
    }
 

To enable decoretors if you want: For Vue Cli if you choose Typescript, everything is ok. Otherwise you need to enable decorators supprot in babel.config.js file.

module.exports = {
  presets: [
    '@vue/app'
  ],
  plugins: [
    [
      "@babel/plugin-proposal-decorators",
      {
        legacy: true
      }
    ]
  ]
}

need to install the following plugin

>> npm install --save-dev @babel/plugin-proposal-decorators

Now let's install Ajwah store and others helping lib

>> npm install rxjs
>> npm install vue-rx
>> npm install ajwah-store
>> npm install ajwah-devtools //optional
>> npm run serve

Let's start with the hello world counterState

counterState using decoretors

import { State, Action, Effect, ofType, Actions } from 'ajwah-react-store';
import { Inc, Dec, AsyncInc } from './actions';
import { updateObject } from './util';
import { mapTo, debounceTime } from "rxjs/operators";
 
@State({
    name: 'counter',
    initialState: { count: 5, msg: '' }
})
class CounterState {
 
    @Action(Inc)
    increment(state, action) {
        return updateObject(state, { count: state.count + 1, msg: '' })
    }
 
    @Action(Dec)
    decrement(state, action) {
        return updateObject(state, { count: state.count - 1, msg: '' })
    }
 
    @Action(AsyncInc)
    asyncIncrement(state, action) {
        return updateObject(state, { msg: 'loading...' })
    }
 
    @Effect()
    ofAsyncInc(action$: Actions) {
        return action$.pipe(
            ofType(ASYNC_INCREMENT),
            debounceTime(1000),
            mapTo({ type: INCREMENT })
        )
 
    }
}
 
export default CounterState;

counterState using convention

import { Actions } from 'ajwah-store';
import { Inc } from "./actions";
import { updateObject } from "../utli";
import { debounceTime, mapTo } from 'rxjs/operators';
 
class CounterSate {
 
    name = 'counter'
    initialState = { count: 10, msg: '' }
 
 
    actionInc(state) {
        return updateObject(state, { count: state.count + 1, msg: '' })
    }
 
    actionDec(state) {
        return updateObject(state, { count: state.count - 1, msg: '' })
    }
 
    actionAsyncInc(state) {
        return updateObject(state, { msg: 'loading...' })
    }
 
    effectForAsyncInc(actions:Actions) {
        return actions.pipe(
            debounceTime(450),
            mapTo({ type: Inc })
        )
    }
}
export default CounterSate;
 

You can choose any style you like or any combination - ajwah support both together

CounterComponent

<template>
  <p>
    <button class="btn" @click="inc()">+</button>
    <button class="btn" @click="dec()">-</button>
    <button class="btn" @click="async_inc()">async(+)</button>
    {{counter.msg||counter.count}}
  </p>
</template>
 
<script>
import { Inc, Dec, AsyncInc } from "../states/actions";
export default {
  name: "Counter",
  subscriptions() {
    return {
      counter: this.storeCtx.select('counter')
    };
  },
 
  methods: {
    inc() {
      this.storeCtx.dispatch({ type: Inc });
    },
    dec() {
      this.storeCtx.dispatch({ type: Dec });
    },
    asyncInc() {
      this.storeCtx.dispatch({ type: AsyncInc });
    }
  }
};
</script>
 
 

Here is the StoreContext API

export declare class StoreContext {
    dispatch(actionName: Action): StoreContext;
    dispatch(actionName: string): StoreContext;
    dispatch(actionName: string, payload?: any): StoreContext;
    addStates(...stateClassTypes: any[]): StoreContext;
    removeStates(...stateNames: string[]): StoreContext;
    removeEffectsByKey(key: string): StoreContext;
    importState(state: any): StoreContext;
    exportState(): Observable<any[]>;
    select<T = any>(pathOrMapFn: ((state: T) => any) | string, ): Observable<any>;
    addEffect<T extends Actions<Action>>(callback: (action$: Actions<Action>, store$?: StoreContext) => Observable<Action>, key?: string): StoreContext;
    addEffects(...effectClassTypes: any[]): StoreContext;
    dispose(): void;
}

using AjwahStore in main file

import Vue from 'vue';
import App from './App.vue';
import router from './router';
 
import vueRx from 'vue-rx';
import { AjwahStore } from 'ajwah-vue-store';
import { devTools } from 'ajwah-devtools';
import counterState from './states/counterState'
 
Vue.use(vueRx);
 
Vue.use(AjwahStore, {
  states: [counterState],
  devTools: devTools()
})
 
Vue.config.productionTip = false;
 
new Vue({
  router,
  render: (h) => h(App),
}).$mount('#app');
 

Counter app - Live

React Doc

Vue Doc

Angular Doc

Note: this lib renamed as ajwah-store

Package Sidebar

Install

npm i ajwah-vue-store

Weekly Downloads

0

Version

0.1.2

License

MIT

Unpacked Size

123 kB

Total Files

55

Last publish

Collaborators

  • jukhan