deduce icon indicating copy to clipboard operation
deduce copied to clipboard

Ridiculously easy JavaScript state containers with action methods.

deduce

NPM version Downloads Build Status Coverage Status Tip

Ridiculously easy JavaScript state containers with reducer methods. Like Redux without all of the boilerplate.

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, reducers) : Store

  • initialState {*}
  • reducers {Object<String,Function>}

Store

.state

Current state of the store.

const store = deduce({ foo: 1 });

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

.addReducers(reducers): Store

  • reducers {Object<String,Function>}

Registers reducers to modify the state. Chainable.

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

.addReducersFor(property, reducers): Store

  • property {String}
  • reducers {Object<String,Function>}

Registers reducers to modify a specific state property. Chainable.

store.addReducersFor('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 fooReducers = {
    [FOO_INCREMENT]: (state = fooInitial, action) {
        return state + action.payload;
    }
};

function foo(state = {}, action) {
    if (action.type in fooReducers) {
        return fooReducers[action.type](state, action);
    }

    return state;
}

function createFooIncrementAction(payload) {
    return {
        type: FOO_INCREMENT,
        payload
    };
}

// bar

const BAR_DECREMENT = 'BAR_DECREMENT';

const barInitial = 0;

const barReducers = {
    [BAR_DECREMENT]: (state = barInitial, action) {
        return state - action.payload;
    }
};

function bar(state, action) {
    if (action.type in barReducers) {
        return barReducers[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 fooReducers = {
    incrementFoo(state = fooInitial, val) {
        return state + val;
    }
};

// bar

const barInitial = 0;

const barReducers = {
    decrementBar(state = barInitial, val) {
        return state - val;
    }
};

// store

import deduce from 'deduce';

const store = deduce()
    .addReducersFor('foo', fooReducers)
    .addReducersFor('bar', barReducers);

// 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

MIT © Shannon Moeller