-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathstore.js
More file actions
108 lines (95 loc) · 4.16 KB
/
store.js
File metadata and controls
108 lines (95 loc) · 4.16 KB
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
101
102
103
104
105
106
107
108
// The `store()` module is a mechanism to store an immutable object that represents state of an application. Any application may have one or more active stores, so there's no limitation from how you use the data. The store itself provides four methods: `state()`, `dispatch(reducer, state)`, `to(cb)`, `remove(cb)`.
//
// 1. The `store.state()` returns a clone of the internal state object, which is simply a pure copy of JSON of the data. Since this uses pure JSON representation in-lieue of actual Tries and immutable data libraries, this keeps the code footprint tiny, but you can only store pure JSON data in the store.
// 2. The `store.to(cb)` will register `cb` as a callback function, invoking `cb(nextState)` whenever the store's state is updated with `store.dispatch()` (`store.remove(cb)` simply does the opposite, removing the callback from the list of event listeners).
// 3. The biggest method implemented by `store()` is `store.dispatch(reducer, state=store.state())`. By default, the second parameter is the existing state of the `store`, but you can override the state object input, if need be. The key here is the redux-inspired `reducer`, which is a function that **you** write that receives two arguments, `state` and `next()`. You should modify the state object somehow, or create a copy, and pass it into `next(state)` to trigger an update to be sent to listener. For example:
//
// ```js
// const logger = (state) => console.log('state changed! -->', state)
// store.to(logger)
//
// store.distpatch((state, next) => {
// setTimeout(() => {
// let timestamp = +new Date
// next({ ...state, timestamp })
// }, 2000)
// })
// ```
import {cancellable} from './fetch'
const clone = (obj) => JSON.parse(JSON.stringify(obj))
/**
*
* Event-driven redux-like updates where we use
* reducer functions to update a singular state object
* contained within the store()
*
* Some limitations: You **must** use plain JS objects and arrays
* with this implementation for serialization and cloning support.
* This could eventually use actual immutable data-structures, but
* an implementation change would be required; however if speed
* or correctness is an issue we can try in the future, as immutable
* libraries use data-structures like tries and the like to reduce
* Garbage Collection and blatant in-memory copies with a "structural
* sharing" technique.
*
* - state()
* - dispatch()
* - to()
* - remove()
*/
export const store = (state={}) => {
// might have to be changed back to Set()
// if subscribers get garbage collected
//
// WeakSet and WeakMap items are GC'd if
// they have no other reference pointing to them
// other than the WeakMap/WeakSet
let subscribers = new Set(),
actions = {}
const instance = {
state: () => clone(state),
dispatch: (reducer, _state=instance.state()) =>
new Promise((res,rej) => {
const next = (newState) => {
state = clone(newState)
for(var s of subscribers){
s(clone(state))
}
res(clone(state))
}
reducer(_state, next)
}),
to: (sub) => subscribers.add(sub),
remove: (sub) => subscribers.delete(sub)
}
return { ...instance, dispatch: cancellable(instance.dispatch) }
}
/*
// Example usage:
// ----------------
let photos = store({photos:[]})
log(photos.state())
const printMe = (state) => {
log('-------------- subscriber called', state)
}
photos.to(printMe)
photos.to(printMe) // can't have duplicate subscribers, printMe only called once per update
photos.to((state) => log('hi'))
const addRandomPhoto = (state, next) => {
setTimeout(() => {
state = {...state, photos: state.photos.concat('https://media0.giphy.com/media/hD52jjb1kwmlO/200w.gif')}
next(state)
}, 1000)
}
setInterval(() => photos.dispatch(addRandomPhoto), 500)
/// example React Component code
//
//
let update = (state) => this.setState(state)
let componentDidMount = () => {
photos.to(update)
}
let componentWillUnmount = () => {
photos.remove(update)
}
*/