a4-reducer
v8.3.0
Published
State Management Reactive Store with Side Effect
Downloads
3
Maintainers
Readme
a4-reducer
Install
npm install a4-reducer
How to setup actions
auth-action.ts
import { Action } from 'a4-reducer';
export interface AuthState {
username: string;
error: any;
busy: boolean;
}
export enum Types {
request_sign_in = 'auth.signin.request',
sign_in_succeed = 'auth.signin.succeed',
sign_in_failed = 'auth.signin.fail',
sign_in_reset = 'auth.signin.reset',
request_sign_out = 'auth.signout.request',
sign_out_succeed = 'auth.signout.succeed',
}
interface SignInRequestPayload {
username: string;
password: string;
};
export class SignInRequest implements Action<SignInRequestPayload> {
readonly type = Types.request_sign_in;
constructor(public payload: SignInRequestPayload) { }
}
interface Token {
token: string;
expiresIn: number;
}
export interface GetOAuthResponse {
lastName: string;
tokenType: string;
token: Token;
}
export interface SignInSucceddPayload extends GetOAuthResponse {
redirect: boolean;
}
export class SignInSucceed implements Action<SignInSucceddPayload> {
readonly type = Types.sign_in_succeed;
constructor(public payload: SignInSucceddPayload) { }
}
export class SignInFail implements Action<void>{
readonly type = Types.sign_in_failed;
readonly payload = null;
}
export class SignInReset implements Action<void> {
readonly type = Types.sign_in_reset;
readonly payload = null;
}
export class SignOutRequest implements Action<void> {
readonly type = Types.request_sign_out;
readonly payload = null;
}
export class SignOutSucceed implements Action<void> {
readonly type = Types.sign_out_succeed;
readonly payload = null;
}
export type Actions = SignInRequest
| SignInSucceed
| SignInFail
| SignInReset
| SignOutSucceed;
How to setup a store
auth-store.ts
import { Reducer, Effect, Store, Action, InitialState } from 'a4-reducer';
import { AuthState, Types, SignInSucceed, SignInFail,
SignInRequest, SignInSucceddPayload, Actions, SignOutSucceed
} from './auth-action';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
@InitialState({
auth: {
username: null,
error: null,
busy: false
}
})
@Injectable({
providedIn: 'root'
})
export class AuthStore extends Store<AuthState> {
constructor(private http: HttpClient, private router: Router) {
super();
}
@Reducer()
reducer(state: Store1State, action: Actions) {
switch (action.type) {
case Types.sign_in_reset:
return { ...state, error: null, busy: false };
case Types.request_sign_in:
return { ...state, busy: true };
case Types.sign_in_succeed:
return { ...state, username: action.payload.lastName };
case Types.sign_in_failed:
return { ...state, error: action.payload, busy: false };
case Types.sign_out_succeed:
return { ...state, username: null };
}
}
@Effect(Types.request_sign_in)
async requestSignIn(action: SignInRequest) {
try {
const result = (await this.http.post<SignInSucceddPayload>('/v1/oauth', action.payload).toPromise());
return new SignInSucceed(result);
} catch (e) {
return new SignInFail(e);
}
}
@Effect(Types.sign_in_succeed)
signInSucceed(action: SignInSucceed) {
document.cookie = `TOKEN=${action.payload.token.token};path=/`;
if (action.payload.redirect === undefined)
this.router.navigate(['/tos']);
}
@Effect(Types.request_sign_out)
requestSignOut(action: Action<void>) {
document.cookie = `TOKEN=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT`;
return new SignOutSucceed();
}
@Effect(Types.sign_out_succeed)
signOutSucceed(action: Action<void>) {
this.router.navigate(['/signin']);
}
}
How to use the store in your app
import { AuthStore } from './auth-store';
import { requestSignIn } from './auth-action';
private username$: Observable<string>;
constructor(private authStore: AuthStore) {
this.username$ = authStore.map(p => p.username);
}
handleSignIn(username: string, password: string) {
this.authStore.dispatch(new requestSignIn({
username,
password
}));
}
To enable console log
import { enableConsoleLog } from 'a4-reducer';
if (!environment.production) {
enableConsoleLog();
}
Difference between .map and .select
Select will only fire the subscription when the current alue is different than the last while Map will fire the subscription when there is a new value set in the store.
How to add side effect without using decorator
import { addSideEffect, removeSideEffect } from 'a4-reducer';
boundHandleThisAction = this.handleThisAction.bind(this);
ngOnInit() {
addSideEffect('ACTION.NAME', this.boundHandleThisAction);
}
ngOnDestroy() {
removeSideEffect('ACTION.NAME', this.boundHandleThisAction);
}
Immutable Methods (protected)
These immutable methods can only be used within the store class.
immutableReplaceElement
Returning a new array with element at index being replaced by newElement.
immutableReplaceElement<T>(array: T[], newElement: T, index: number): T[]
immutableRemoveElement
Returning a new array with element at index being removed.
immutableRemoveElement<T>(array: T[], index: number): T[]
immutableInsertElement
Returning a new array with element being inserted at specified index.
immutableInsertElement<T>(array: T[], element: T, index: number): T[]
immutablePrependElement
Returning a new array with element inserted at the start (first element) of the given array.
immutablePrependElement<T>(array: T[], element: T): T[]
immutableAppendElement
Returning a new array with element inserted at the end (last element) of the given array.
immutableAppendElement<T>(array: T[], element: T): T[]