# a4-reducer

> State Management Reactive Store with Side Effect

Latest version **8.3.0** (published 2019-07-20) · MIT license · 0 weekly downloads

## Install

```sh
npm install a4-reducer
pnpm add a4-reducer
yarn add a4-reducer
bun add a4-reducer
```

## Health

**Score 25/100 (F)** — status: abandoned.

Positive: has types; esm support; no vulnerabilities.

Warnings: low downloads.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 8.3.0 |
| Published | 2019-07-20 |
| First published | 2019-06-21 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 1 |
| Unpacked size | 243.7 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Bill Cheng |
| Maintainers | cccheng |
| Keywords | state, management, store, angular, side effect |

## Links

- npm: https://www.npmjs.com/package/a4-reducer
- npm.io page: https://npm.io/package/a4-reducer

## Dependencies (1)

- [tslib](https://npm.io/package/tslib.md) ^1.9.0

## Alternatives

- [@reckona/mreact-store](https://npm.io/package/@reckona/mreact-store.md) — 976 weekly downloads
- [regular-state](https://npm.io/package/regular-state.md) — 410 weekly downloads
- [@pacote/flux-actions](https://npm.io/package/@pacote/flux-actions.md) — 65 weekly downloads
- [@pilotlab/lux-debug](https://npm.io/package/@pilotlab/lux-debug.md) — 39 weekly downloads
- [vue-persist-state](https://npm.io/package/vue-persist-state.md) — 19 weekly downloads

## Recent versions

- 8.3.0 (latest) — 2019-07-20
- 8.2.2 — 2019-06-26
- 8.2.1 — 2019-06-22
- 8.2.0 — 2019-06-22
- 8.1.0 — 2019-06-22
- 8.0.0 — 2019-06-21

## README

# a4-reducer

## Install
```bash
npm install a4-reducer
```

## How to setup actions
### auth-action.ts
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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.
```typescript
immutableReplaceElement<T>(array: T[], newElement: T, index: number): T[]
```

## immutableRemoveElement
Returning a new array with element at index being removed.
```typescript
immutableRemoveElement<T>(array: T[], index: number): T[]
```

## immutableInsertElement
Returning a new array with element being inserted at specified index.
```typescript
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.
```typescript
immutablePrependElement<T>(array: T[], element: T): T[]
```

## immutableAppendElement
Returning a new array with element inserted at the end (last element) of the given array.
```typescipt
immutableAppendElement<T>(array: T[], element: T): T[]
```

---
_Source: https://npm.io/package/a4-reducer · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
