2.0.14 • Published 5 years ago

terse-reduce v2.0.14

Weekly downloads
2
License
MIT
Repository
github
Last release
5 years ago

Terse-Reduce

Javascript / Typescript state management with a special emphasis on:

  • easy integration with and incremental migration from traditional Redux reducers.
  • handling immutable updates through a mutable API powered by Immer
  • minimal boilerplate & maximum readability
  • strong typesafety for Typescript and strong autocompletion / refactoring support for Javascript (VSCode highly recommended)

Installation

yarn add terse-reduce

Examples

NOTE: All below code is written in Typescript. Javascript users can simply remove all Typescript interface and type declarations

Example 1: with sinlge 'root' reducer

import { createStore, Store } from 'redux';
import { manageRoot, initialize } from 'terse-reduce';
import axios from 'axios';

interface AppState { num: number, msg: string }
interface ApiError { status: number; message: string; }

const calculator = manageRoot<AppState>()
  .startWithState({ 
    num: 0,
    msg: ''
  })
  .processActions(take => ({
    ADD: take<number>()
      .update(_ => _.change.num += _.payload),
    SUBTRACT: take<number>()
      .update(_ => _.change.num -= _.payload),
    ADD_PI: take<number>()
      .update(_ => _.change.msg = `Calculating PI to ${_.payload} decimal places...`)
      .promise(_ => axios.get<number>(`${apiUrl}/calculate/pi?decimal-places=${_.payload}`))
      .update(_ => { _.change.num += _.promiseResult.data; _.change.msg = ''; })
      .catch<ApiError>(_ => _.promiseError.status === 400)
      .update(_ => _.change.notificationMessage = _.promiseRejection.message)
    })
  });

const { reducer, initialState } = initialize<AppState>(() => store.dispatch, calculator);
const store = createStore(reducer, initialState);  

calculator.ADD.dispatch(5);  

Example 2: with multiple 'branch' reducers

This is effecively the same as using 1 reducer per branch and then applying combineReducers()

interface Branch1State { propStr: string; propBool: boolean; }
interface Branch2State { propNum: number; propArr: number[]; }
interface AppState { branch1: Branch1State, branch2: Branch2State }

const branch1Manager = manageBranch<AppState, Branch1State>('branch1')
  .startWithState({
    propStr: '',
    propBool: false
  })
  .processActions(take => ({
    ACTION_ONE: take<string>()
      .update(_ => _.change.propStr = _.payload)
  }));

const branch2Manager = manageBranch<AppState, Branch2State>('branch2')
  .startWithState({
    propNum: 0,
    propArr: []
  })
  .processActions(take => ({
    ACTION_TWO: take<number>()
      .update(_ => _.change.propNum = _.payload)
  }));

const { reducer, initialState } = initialize<AppState>(() => store.dispatch, branch1Manager, branch2Manager);
const store: Store<AppState> = createStore(reducer, initialState);      

branch1Manager.ACTION_ONE.dispatch('Bake Bread');  

Example 3: Creating a re-usable chain of operations

Sometimes, one might want to create a re-usable chain of operations to be performed.
This is useful for highly repetitive logic, particularly when promises are involved.
For this we have takeRoot() and takeBranch() functions. Below is an example of takeRoot().

import { manageRoot, initialize, takeRoot } from "../src";
import { createStore, Store } from 'redux';
import axios from 'axios';

interface User { id: number, name: string, age: number }
interface Company { id: number, name: string }
interface AppState { showUiLoader: boolean, notificationMessage: string, users: User[], companies: Company[] }
interface ApiError { status: number; message: string; }

const postToApi = <P extends any[]>(endpoint: string, getArray: (s: AppState) => P) =>
  takeRoot<AppState, P>()
    .promise(_ => axios.post<P>(`/api/${endpoint}`, { body: _.payload }))
    .update(_ => getArray(_.change).push(_.promiseResult))
    .catch<ApiError>(_ => _.promiseError.status === 400)
    .update(_ => _.change.notificationMessage = _.promiseRejection.message);

const manageState = manageRoot<AppState>()
  .processActions(take => ({
    SAVE_USER: postToApi('/user', s => s.users),
    SAVE_COMPANY: postToApi('/company', s => s.users)
  }))
2.0.14

5 years ago

2.0.13

6 years ago

2.0.12

6 years ago

2.0.11

6 years ago

2.0.10

6 years ago