node package manager

deduce

deduce

NPM version Downloads Build Status Coverage Status Tip

Ridiculously easy JavaScript state containers with action methods. Like Redux without all the switch statements.

Install

npm install --save deduce

Usage

// reducers.js 
 
export function increment(state, val = 1) {
    return state + val;
}
 
export function decrement(state, val = 1) {
    return increment(state, -val);
}
 
// store.js 
 
import deduce from 'deduce';
import * as reducers from './reducers';
 
const store = deduce(1, reducers);
 
store.addListener(() => {
    console.log(store.state);
});
 
store.increment();  // -> 2 
store.increment(2); // -> 4 
store.decrement();  // -> 3 
store.decrement(2); // -> 1 

API

deduce(initialState, actions) : Store

  • initialState *
  • actions Object<String,Function>

Store

.state

Current state of the store.

const store = deduce({ foo: 1 });
 
console.log(store.state); // -> { foo: 1 } 

.addActions(actions): Store

  • actions Object<String,Function>

Registers actions to modify the state. Chainable.

store.addActions({
    increment(state, val) {
        return {
            ...state,
            foo: state.foo + val,
        };
    },
});

.addActionsFor(property, actions): Store

  • property String
  • actions Object<String,Function>

Registers actions to modify a specific state property. Chainable.

store.addActionsFor('foo', {
    increment(state, val) {
        return state + val;
    },
});

.addListener(callback): Function

  • callback Function

Adds a listener to be called any time the state is updated. Returns a function to remove the listener.

const removeListener = store.addListener(() => {
    console.log(store.state);
});
 
store.increment();

Why?

The typical Redux patterns entail a lot of boilerplate. The documented and accepted patterns for reducing boilerplate really just swap one kind for another:

Redux Example

Consider the following Redux example that creates a store with two numbers: foo which may be incremented and bar which may be decremented.

// foo 
 
const FOO_INCREMENT = 'FOO_INCREMENT';
 
const fooInitial = 0;
 
const fooActions = {
    [FOO_INCREMENT]: (state = fooInitial, action) {
        return state + action.payload;
    }
};
 
function foo(state = {}, action) {
    if (action.type in fooActions) {
        return fooActions[action.type](state, action);
    }
 
    return state;
}
 
function createFooIncrementAction(payload) {
    return {
        type: FOO_INCREMENT,
        payload
    };
}
 
// bar 
 
const BAR_DECREMENT = 'BAR_DECREMENT';
 
const barInitial = 0;
 
const barActions = {
    [BAR_DECREMENT]: (state = barInitial, action) {
        return state - action.payload;
    }
};
 
function bar(state, action) {
    if (action.type in barActions) {
        return barActions[action.type](state, action);
    }
 
    return state;
}
 
function createBarDecrementAction(payload) {
    return {
        type: BAR_DECREMENT,
        payload
    };
}
 
// store 
 
import { createStore, combineReducers } from 'redux';
 
const reducer = combineReducers({ foo, bar });
const store = createStore(reducer, {});
 
// application 
 
store.dispatch(createFooIncrementAction(1));
store.dispatch(createBarDecrementAction(1));
 
console.log(store.getState());
// { 
//   foo: 1, 
//   bar: -1 
// } 

Split that up into modules and you can see how new-comers could easily be overwhelmed when the underlying principles are beautifully clean and simple.

Deduce Example

Compare the above with this deduce example that does the same thing:

// foo 
 
const fooInitial = 0;
 
const foo = {
    incrementFoo(state = fooInitial, val) {
        return state + val;
    }
};
 
// bar 
 
const barInitial = 0;
 
const bar = {
    decrementBar(state = barInitial, val) {
        return state - val;
    }
};
 
// store 
 
import deduce from 'deduce';
 
const store = deduce()
    .addActionsFor('foo', foo)
    .addActionsFor('bar', bar);
 
// application 
 
store.incrementFoo(1);
store.decrementBar(1);
 
console.log(store.state);
// { 
//   foo: 1, 
//   bar: -1 
// } 

Contribute

Standards for this project, including tests, code coverage, and semantics are enforced with a build tool. Pull requests must include passing tests with 100% code coverage and no linting errors.

Test

$ npm test

© Shannon Moeller me@shannonmoeller.com (http://shannonmoeller.com)

Licensed under MIT