-
Notifications
You must be signed in to change notification settings - Fork 139
/
index.js
100 lines (89 loc) 路 2.85 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { assign } from './util';
/**
* Creates a new store, which is a tiny evented state container.
* @name createStore
* @param {Object} [state={}] Optional initial state
* @returns {store}
* @example
* let store = createStore();
* store.subscribe( state => console.log(state) );
* store.setState({ a: 'b' }); // logs { a: 'b' }
* store.setState({ c: 'd' }); // logs { a: 'b', c: 'd' }
*/
export default function createStore(state) {
let listeners = [];
state = state || {};
function unsubscribe(listener) {
let out = [];
for (let i=0; i<listeners.length; i++) {
if (listeners[i]===listener) {
listener = null;
}
else {
out.push(listeners[i]);
}
}
listeners = out;
}
function setState(update, overwrite, action) {
state = overwrite ? update : assign(assign({}, state), update);
let currentListeners = listeners;
for (let i=0; i<currentListeners.length; i++) currentListeners[i](state, action);
}
/**
* An observable state container, returned from {@link createStore}
* @name store
*/
return /** @lends store */ {
/**
* Create a bound copy of the given action function.
* The bound returned function invokes action() and persists the result back to the store.
* If the return value of `action` is a Promise, the resolved value will be used as state.
* @param {Function} action An action of the form `action(state, ...args) -> stateUpdate`
* @returns {Function} boundAction()
*/
action(action) {
function apply(result) {
setState(result, false, action);
}
// Note: perf tests verifying this implementation: https://esbench.com/bench/5a295e6299634800a0349500
return function() {
let args = [state];
for (let i=0; i<arguments.length; i++) args.push(arguments[i]);
let ret = action.apply(this, args);
if (ret!=null) {
if (ret.then) return ret.then(apply);
return apply(ret);
}
};
},
/**
* Apply a partial state object to the current state, invoking registered listeners.
* @param {Object} update An object with properties to be merged into state
* @param {Boolean} [overwrite=false] If `true`, update will replace state instead of being merged into it
*/
setState,
/**
* Register a listener function to be called whenever state is changed. Returns an `unsubscribe()` function.
* @param {Function} listener A function to call when state changes. Gets passed the new state.
* @returns {Function} unsubscribe()
*/
subscribe(listener) {
listeners.push(listener);
return () => { unsubscribe(listener); };
},
/**
* Remove a previously-registered listener function.
* @param {Function} listener The callback previously passed to `subscribe()` that should be removed.
* @function
*/
unsubscribe,
/**
* Retrieve the current state object.
* @returns {Object} state
*/
getState() {
return state;
}
};
}