ajwah-vue-store
v0.1.2
Published
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.
Downloads
2
Readme
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');
Note: this lib renamed as ajwah-store