1 | export const createStore = (state) => {
|
2 | let listeners = [];
|
3 | function unsubscribe(listener) {
|
4 | let out = [];
|
5 | for (let i = 0; i < listeners.length; i++) {
|
6 | if (listeners[i] === listener) {
|
7 | listener = null;
|
8 | }
|
9 | else {
|
10 | out.push(listeners[i]);
|
11 | }
|
12 | }
|
13 | listeners = out;
|
14 | }
|
15 | function setState(update, overwrite) {
|
16 | state = overwrite ? update : Object.assign(Object.assign({}, state), update);
|
17 | let currentListeners = listeners;
|
18 | for (let i = 0; i < currentListeners.length; i++) {
|
19 | currentListeners[i](state);
|
20 | }
|
21 | }
|
22 | /**
|
23 | * An observable state container, returned from {@link createStore}
|
24 | * @name store
|
25 | */
|
26 | return {
|
27 | get state() {
|
28 | return state;
|
29 | },
|
30 | /**
|
31 | * Create a bound copy of the given action function.
|
32 | * The bound returned function invokes action() and persists the result back to the store.
|
33 | * If the return value of `action` is a Promise, the resolved value will be used as state.
|
34 | * @param {Function} action An action of the form `action(state, ...args) -> stateUpdate`
|
35 | * @returns {Function} boundAction()
|
36 | */
|
37 | action(action) {
|
38 | function apply(result) {
|
39 | setState(result, false);
|
40 | }
|
41 | // Note: perf tests verifying this implementation: https://esbench.com/bench/5a295e6299634800a0349500
|
42 | return function () {
|
43 | let args = [state];
|
44 | for (let i = 0; i < arguments.length; i++)
|
45 | args.push(arguments[i]);
|
46 | // @ts-ignore
|
47 | let ret = action.apply(this, args);
|
48 | if (ret != null) {
|
49 | if (ret.then)
|
50 | return ret.then(apply);
|
51 | return apply(ret);
|
52 | }
|
53 | };
|
54 | },
|
55 | /**
|
56 | * Apply a partial state object to the current state, invoking registered listeners.
|
57 | * @param {Object} update An object with properties to be merged into state
|
58 | * @param {Boolean} [overwrite=false] If `true`, update will replace state instead of being merged into it
|
59 | */
|
60 | setState,
|
61 | /**
|
62 | * Register a listener function to be called whenever state is changed. Returns an `unsubscribe()` function.
|
63 | * @param {Function} listener A function to call when state changes. Gets passed the new state.
|
64 | * @returns {Function} unsubscribe()
|
65 | */
|
66 | subscribe(listener) {
|
67 | listeners.push(listener);
|
68 | return () => {
|
69 | unsubscribe(listener);
|
70 | };
|
71 | }
|
72 | // /**
|
73 | // * Remove a previously-registered listener function.
|
74 | // * @param {Function} listener The callback previously passed to `subscribe()` that should be removed.
|
75 | // * @function
|
76 | // */
|
77 | // unsubscribe,
|
78 | };
|
79 | };
|