deduce
v2.0.0-2
Published
Ridiculously easy JavaScript state containers with reducer methods.
Downloads
33
Maintainers
Readme
deduce
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