dalefrancis88 / redux-lens

MIT License
0 stars 0 forks source link

redux-lens npm version Build Status

Apply a redux reducer and an action at the specified path of your application state tree (i.e. redux store).

Install

$ npm install --save redux-lens
$ yarn add redux-lens

Usage

const {createStore} = require('redux');
const {createReducer, reduceIn, path} = require('redux-lens');

const defaultReducer = (state) => {
    console.log('defaultReducer called');
    return state;
};

const initialState = {
    foo: {
        bar: {
            baz: [1, 42]
        }
    },
    counter: 9000
};

const {reducer, path as pathCreator} = createReducer({
    reducer: defaultReducer,
    aliases: {
        plusTwo(state = 0, action) {
            switch(action.type) {
                case 'ADD': {
                    return state + 2;
                }
            }

            return state;
        }
    }
});

const store = createStore(reducer, initialState);

const addReduce = (state = 0, action) => {
    switch(action.type) {
        case 'ADD': {
            return state + 1;
        }
    }

    return state;
};

const addAction = {
    type: 'ADD'
};

store.getState();
// => {
//     foo: {
//         bar: {
//             baz: [1, 42]
//         }
//     },
//     counter: 9000
// };

// Using an alias.
store.dispatch(reduceIn(['foo', 'bar', 'baz', 1], 'plusTwo', addAction));
// => {
//     foo: {
//         bar: {
//             baz: [1, 44]
//         }
//     },
//     counter: 9000
// };

store.dispatch(reduceIn(['counter'], addReduce, addAction));
// => {
//     foo: {
//         bar: {
//             baz: [1, 44]
//         }
//     },
//     counter: 9001
// };

// Create a lens path
const pathToCounter = path(['counter']);

store.dispatch(reduceIn(pathToCounter, addReduce, addAction));
// => {
//     foo: {
//         bar: {
//             baz: [1, 44]
//         }
//     },
//     counter: 9002
// };

API

createReducer(options)

options = {
    // Function signature: fallback_reducer(state, action) => next_state
    //
    // redux compatible reducer function (optional)
    reducer: fallback_reducer,

    // Function signature: getter(path, state) => value
    //
    // function to get the value at "path" of "state". (optional)
    get: getter,

    // Function signature: setter(path, new_value, state) => next_state
    //
    // function to set "new_value" at "path" of "state". (optional)
    set: setter,

    // Plain object mapping aliases to redux reducers. (optional)
    aliases: {
        alias: reducer
    }
}

Returns:

reduceIn(path, reducer, action) => action

reduceIn is an action creator, which generates an action that would be consumed by the reducer generated by createReducer(options).

path(path_to_state, getter = optional, setter = optional) => lensPath

Creates a function (i.e. lensPath) whose focus is at the specified path, path_to_state.

Similar to: http://ramdajs.com/docs/#lensPath

Type signature of lensPath:

lensPath :: Functor f => P -> getter -> setter -> lens

lens :: Functor f => intoFunctor -> R -> f U
getter :: P -> R -> T (default to `path` function)
setter :: P -> U -> R -> R' (default ot `assocPath` function)
intoFunctor :: Functor f => T -> f T

P := path
R := root
T := value_at_path
U := newValue
R' := newRoot
f T := Functor<T>
f U := Functor<U>

Functor<T> := {
    value: T,
    map: mapf -> Functor<U>
}
mapf :: T -> U

To learn more about lenses, see: https://medium.com/@dtipson/functional-lenses-d1aba9e52254

Custom set / get

If you do not assign custom set / get functions via createReducer(options), then state is assumed to be a plain object type.

Development

Credits

Credits to the following, of which I've refactored out lens functions into ~/src/lens.js:

License

MIT.