Library to listen/observe/watch changes of Redux store state.
Installation ↑
npm install listate
bower install listate
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 ↑
import listen from 'listate';
// Or if you need extra functionality
import extListen from 'listate/extra';
const listen = require('listate').listen;
// Or if you need extra functionality
const extListen = require('listate/extra').listen;
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;
});
<!-- 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 ↑
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 ↑
Checks whether current value (state) is not equal previous value (state).
Returns value of the following comparison: state !== prevState
.
Adds/registers state change listener for the given store.
Arguments:
-
store: object
- Store for which listener should be added/registered. -
listener: Function | object
- Specifies listener that should be called on a state change. Can be a function or an object that defines listener settings/details. -
listener.handle: Function
- Listener that should be called on a state change. -
listener.context: boolean | object
(optional) - Specifies object that should be used asthis
value when calling the listener. -
listener.data: any
(optional) - Any data that should be passed into the listener. -
listener.delay: number
(optional) - Specifies that listener should be called after the given number of milliseconds have elapsed. Works similar todebounce
: when several requests for the listener call arrive during the specified period only the last one will be applied after the timeout.0
means that the listener should be called asynchronuosly. -
listener.filter: (state) => state.part
(optional) - Function (selector) to extract state part which will be used insidewhen
to determine whether the listener should be called. By default the entire state will be used. -
listener.once: boolean
(optional) - Whether the listener should be called just once (by defaultfalse
). -
listener.when: (current, prev, data) => boolean
(optional) - Function to determine whether the listener should be called. By defaultbaseWhen
is used. The listener will be called if the function returns true. The following parameters will be passed into the function:- The current state or a part of the current state if
filter
is set. - The previous state or a part of the previous state if
filter
is set. - An object that will be passed into listener.
- The current state or a part of the current state if
Returns a function that removes/unsubscribes the listener.
An object with the following fileds will be passed as parameter into the listener:
current: any
- The current state or a part of the current state iffilter
is set.prev: any
- The previous state or a part of the previous state iffilter
is set.state: object
- The current state.prevState: object
- The previous state.data: any
- The auxiliary data (value oflistener.data
parameter).store: object
- The store for which listener is registered.dispatch: Function
- Reference todispatch
method of the store.unlisten: Function
- The function that removes/unsubscribes the listener.
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
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}
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
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'}
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
Check whether current object (state) is not equal previous object (state) deeply comparing values of their fields.
The same as unlike(state, prevState, true)
.
Add/register state change listener for the given store.
It is a wrap around base listate.listen
that supports the following enhanced listener settings:
listener.filter
. When an array or an object is passed, the used filter will be result ofgetPartFilter(listener.filter)
. When a string is passed, the used filter will be result ofgetFieldFilter(listener.filter)
.listener.when
. By defaultunlike
is used.
See doc
folder for details.
Contributing ↑
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 ↑
Copyright (c) 2017-2019 Denis Sikuler
Licensed under the MIT license.