ridof

Simple isomorphic state manager

Usage no npm install needed!

<script type="module">
  import ridof from 'https://cdn.skypack.dev/ridof';
</script>

README

Coverage Status Build Status

Ridof

Simple isomorphic state manager

Install it

@ yarn add ridof

Run the tests coverage

@ cd node_modules/ridof
@ yarn && yarn build && yarn cover

Use it

Create a store

const Ridof = require('ridof');

const initialState = {
    num: 1,
    name: 'Federico'
}
// The reducer function
// params holds all the values passed to dispatch but the type
const reducer = (oldState, action, params) => {
    const newState = Object.assign({}, oldState)
    switch (action) {
        case 'INCREMENT':
            newState.num += 1;
            break;
        case 'DECREMENT':
            newState.num -= 1;
            break;
        case 'POW':
            newState.num *= newState.num;
            break;
        case 'RENAME':
            newState.name = params.name || 'no name given';
            break;
    }
    return newState;
}

// initialState is optional, default is {}
const Store = Ridof.getStore(reducer, initialState);
Store.subscribe((oldState, newState, action) => {
    console.log(newState);
})
Store.dispatch({type: 'INCREMENT'}) // -> {num: 2, name: 'Federico'}
Store.dispatch({type: 'INCREMENT'}) // -> {num: 3, name: 'Federico'}
Store.dispatch({type: 'POW'}) // -> {num: 9, name: 'Federico'}
Store.dispatch({type: 'DECREMENT'}) // -> {num: 8, name: 'Federico'}
Store.dispatch({type: 'POW'}) // -> {num: 64, name: 'Federico'}
Store.dispatch({type: 'INCREMENT'}) // -> {num: 65, name: 'Federico'}
Store.dispatch({type: 'POW'}) // -> {num: 4225, name: 'Federico'}
Store.dispatch({type: 'RENAME'}) // -> {num: 4225, name: 'no name given'}
Store.dispatch({type: 'RENAME', name: 'Foo'}) // -> {num: 4225, name: 'Foo'}
...

Time travel

Check the states and time travel. It works exactly like the undo redo of any editor:
Suppose the state history is [s0, s1, ... si, ... sn] where sn is the current state. We can navigate back and forth within that range, and suppose we go back to si, if now we dispatch an action that will bring to sx then all states from s(i+1) to sn will be lost and the new history will be [s0, s1, .... si, sx]


Store functions

Creates a store given one reducer function

const Store = Ridof.getStore(reducer, [initialStatus || {}]);

the reducer function will receive the following:

  • state: the current state
  • action: the action label
  • params: all passed to ‘dispatch’ but the type

Return the current state

const currentState = Store.getState();

Add a subscriber function, will receive the following params:

  • oldState: the old state
  • newState: the new state
  • action: the specific action type dispatched
Store.subscribe(subscriber);

Dispatch an action:

Store.dispatch({
    type:'INCREMENT', // needs at least a type field
    all: 'others',
    params: 'follows'
});

a second Boolean parameter is accepted by the dispatch; when true it allows to add (after reducer action on the state) the parameters passed that are missing on the state:

// { num: 0 }
Store.dispatch({
    type:'INCREMENT', // this action only increments `num`
    all: 'others',
    params: 'follows'
}, true); // it is passed so missing ones will be added
// { num: 0, all: 'others', params: 'follows'}

Move in the states:

Store.move(i);

The integer passed has to be negative to go back, positive to go forward. So for example we can go back two step calling Store.move(-2) then, if we do NOT dispatch an action, we could move forward of 1 or 2 steps. In case we target a state that is not indexed in the history array then there will be no effects.


Replace the reducer function

Store.replaceReducer(newReducer);

Reset state to the initialState, clears history and subscribers

Store.reset();

Combine two or more reducers

var reducer = Ridof.combine({
    mods: (state = [], action, params) => {
        const newState = [...state];
        switch (action) {
            case 'ADDMOD': newState.push(params.name); break;
            default:;
        }
        return newState;
    },
    plugins: (state = [], action, params) => {
        const newState = [...state];
        switch (action) {
            case 'ADDPLUGIN': newState.push(params.name); break;
            default:;
        }
        return newState;
    }
});
var store = Ridof.getStore(reducer);
store.subscribe((oldState, newState, action) => {
    if (action === 'END') {
        console.log(store.getState())
        /*
        {   
            mods: ['mymod1', 'mymod2'],
            plugins: ['myplugin']
        }
        */
    }
});
store.dispatch({ type: 'ADDPLUGIN', name: 'myplugin' });
store.dispatch({ type: 'ADDMOD', name: 'mymod1' });
store.dispatch({ type: 'ADDMOD', name: 'mymod2' });
store.dispatch({ type: 'END' });

Restrict state transitions

From version 1.3.0 is possible to restrict the state transitions passing to getStore a third config parameter as a function:

Ridof.getStore(reducer, initState, (currentTag, nextTag, state, action) => {
    // here tags are corresponds to action types
    // let's say we want only one plugin to be added
    if (currentTag === 'ADDPLUGIN' && nextTag === 'ADDPLUGIN') return false
    return true
});