Home

Awesome

listate <a name="start"></a>

Library to listen/observe/watch changes of Redux store state.

NPM version Build Status

Table of contents

Installation <a name="install"></a>

Node

npm install listate

Bower

bower install listate

AMD, <script>

Use dist/listate.js or dist/listate.min.js (minified version). Use dist/extra.js or dist/extra.min.js (minified version) to apply extra functions.

Usage <a name="usage"></a>

ECMAScript 6

import listen from 'listate';
// Or if you need extra functionality
import extListen from 'listate/extra';

Node

const listen = require('listate').listen;
// Or if you need extra functionality
const extListen = require('listate/extra').listen;

AMD

define(['path/to/dist/listate.js', 'path/to/dist/extra.js'], function(listate, extra) {
    const listen = listate.listen;
    // Import extra.js if you need extra functionality
    const extListen = extra.listen;
});

Bower, <script>

<!-- Use bower_components/listate/dist/listate.js and bower_components/listate/dist/extra.js if the library was installed by Bower -->
<script type="text/javascript" src="path/to/dist/listate.js"></script>
<!-- Or if you need extra functionality -->
<script type="text/javascript" src="path/to/dist/extra.js"></script>
<script type="text/javascript">
    // listate is available via listate field of window object
    const listen = listate.listen;
    // Extra functionality is available inside extra namespace
    const extListen = listate.extra.listen;
</script>

Examples <a name="examples"></a>

import { createStore } from 'redux';
import listen from 'listate';
import extListen from 'listate/extra';

const initState = {
    user: null,
    section: '',
    map: {
        main: {}
    }
};

function reducer(state, action) {
    const { payload } = action;
    let newState;
    switch (action.type) {
        case 'AUTH':
            return Object.assign({}, state, {user: payload});
        case 'SELECT_SECTION':
            return Object.assign({}, state, {section: payload});
        case 'SET_SECTION':
            newState = Object.assign({}, state);
            newState.map = Object.assign({}, state.map);
            newState.map[payload.key] = payload.value;
            return newState;
        default:
            return state;
    }
}

const store = createStore(reducer, initState);

listen(store, {
    data: 'main',
    filter: (state) => state.user,
    // One-time listener
    once: true,
    handle(data) {
        // Dispatch any action
        data.dispatch({
            type: 'SELECT_SECTION',
            // data.current === state.user, data.data === 'main'
            payload: data.current.favoriteSection || localStorage.getItem('selectedSection') || data.data
        });
    }
});
listen(store, {
    filter: (state) => state.section,
    when: (current, prev) => current !== prev && current !== 'exit',
    // Call the listener no more frequently than once per second
    delay: 1000,
    handle(data) {
        // data.current === state.section
        localStorage.setItem('selectedSection', data.current);
        console.log('Saved section: ', data.current);
    }
});
listen(store, {
    description: 'map change listener',
    context: true,
    filter: (state) => state.map,
    when: (current, prev, data) => current.stat && data.state.user && data.state.section === 'video',
    handle(data) {
        console.log('data.prev:', data.prev);   // {main: {}}
        console.log('data.current:', data.current);   // {main: {}, stat: {a: 1}}
        console.log('this.description:', this.description);   // map change listener
    }
});
extListen(store, {
    filter: {s: 'section', main: 'map.main'},
    handle(data) {
        console.log('extListen: data.prev -', data.prev);
        console.log('extListen: data.current -', data.current);
    }
});
...
store.dispatch({
    type: 'AUTH',
    payload: {login: 'commander'}
});
...
store.dispatch({
    type: 'SELECT_SECTION',
    payload: 'video'
});
...
store.dispatch({
    type: 'SET_SECTION',
    payload: {
        key: 'stat',
        value: {
            a: 1
        }
    }
});
...
store.dispatch({
    type: 'SELECT_SECTION',
    payload: 'news'
});
...
store.dispatch({
    type: 'SET_SECTION',
    payload: {
        key: 'main',
        value: {
            content: 'text'
        }
    }
});

API <a name="api"></a>

Base functionality (listate, dist/listate.js)

baseWhen(state, prevState): boolean

Checks whether current value (state) is not equal previous value (state).

Returns value of the following comparison: state !== prevState.

listen(store, listener): Function

Adds/registers state change listener for the given store.

Arguments:

Returns a function that removes/unsubscribes the listener.

An object with the following fileds will be passed as parameter into the listener:

Extra functionality (listate/extra, dist/extra.js)

getPathValue(obj, path): any

Return value of specified field path inside given object.

import { getPathValue } from 'listate/extra';
const obj = {
    a: {
        b: {
           c: 'value'
        },
        d: true
    },
    e: 4,
    f: [1, 'z', null]
};
getPathValue(obj, 'a.b.c');   // 'value'
getPathValue(obj, 'a.c');   // undefined

getObjectPart(source, parts): object

Create an object containing specified parts of the given object.

import { getObjectPart } from 'listate/extra';
const obj = {
    a: {
        b: {
           c: 'value',
           d: true
        },
        e: 4,
        f: [1, 'z', null]
    },
    g: 7,
    h: {
        i: false,
        j: 0
    },
    k: 'king',
    l: 'last'
};
getObjectPart(obj, {f1: 'a.b.d', f2: 'a.f.1', f3: 'g', f4: 'h.j'});   // {f1: true, f2: 'z', f3: 7, f4: 0}

getFieldFilter(path): Function

Return a function that extracts value of the specified field path inside a given object.

import { getFieldFilter } from 'listate/extra';
const filter = getFieldFilter('a.d');
const obj = {
    a: {
        b: {
           c: 'value'
        },
        d: 17
    },
    e: 4,
    f: [1, 'z', null]
};
filter(obj);   // 17

getPartFilter(parts): Function

Return a function that creates an object containing the specified parts of a given object.

import { getPartFilter } from 'listate/extra';
const filter = getPartFilter({f1: 'a.b.c', f2: 'h.j', f3: 'k'});
const obj = {
    a: {
        b: {
           c: 'value',
           d: true
        },
        e: 4,
        f: [1, 'z', null]
    },
    g: 7,
    h: {
        i: false,
        j: 0
    },
    k: 'king',
    l: 'last'
};
filter(obj);   // {f1: 'value', f2: 0, f3: 'king'}

unlike(state, prevState, deep): boolean

Check whether current object (state) is not equal previous object (state) comparing values of their fields.

import { unlike } from 'listate/extra';
unlike({a: 1, b: 2}, {a: 1, b: 2});   // false
unlike({a: 1, b: {c: 3}}, {a: 1, b: {c: 3}});   // true
unlike({a: 1, b: {c: 3}}, {a: 1, b: {c: 3}}, true);   // false

unlikeDeep(state, prevState): boolean

Check whether current object (state) is not equal previous object (state) deeply comparing values of their fields.

The same as unlike(state, prevState, true).

listen(store, listener): Function

Add/register state change listener for the given store.

It is a wrap around base listate.listen that supports the following enhanced listener settings:

See doc folder for details.

Contributing <a name="contributing"></a>

In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code.

License <a name="license"></a>

Copyright (c) 2017-2019 Denis Sikuler
Licensed under the MIT license.