Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A stateless store version of alt
import { Dispatcher } from 'flux'
function isPromise(obj) {
return obj && (typeof obj === 'object' || typeof obj === 'function') &&
typeof obj.then === 'function'
}
const inject = Math.random().toString(16).substr(2, 7)
class Alt {
constructor(options = {}) {
this.dispatcher = new options.Dispatcher()
this.serialize = options.serialize || JSON.stringify
this.deserialize = options.deserialize || JSON.parse
this.actions = {}
this.stores = {}
this.history = options.history || 2
this.payloads = []
this.state = this.serialize({})
}
createActions(namespace, model) {
this.actions[namespace] = Object.keys(model).reduce((actions, name) => {
const action = (...args) => {
const data = model[name](...args)
const meta = { namespace, name, args }
if (isPromise(data)) {
data
.then(x => this.dispatcher.dispatch({ action, data: x, meta }))
.catch(e => this.dispatcher.dispatch({ action: null, data: e, meta }))
} else {
this.dispatcher.dispatch({ action, data, meta })
}
}
actions[name] = action
return actions
}, {})
return this.actions[namespace]
}
createStore(namespace, reducer, initialState, ...args) {
const subscriptions = []
let state = initialState
this.payloads[namespace] = []
// save the state to app level state
const saveState = state => {
const appState = this.deserialize(this.state)
appState[namespace] = state
this.state = this.serialize(appState)
if (this.payloads.length - 1 < this.history) {
this.payloads[namespace].push(state)
}
}
// dispatch event
this.dispatcher.register((payload) => {
state = reducer(state, payload, ...args)
subscriptions.forEach(f => f(state))
})
// our store
const store = {
name: namespace,
subscribe: (onChange) => {
const id = subscriptions.push(onChange)
return () => subscriptions.splice(id, 1)
},
peek: (n = 1) => {
const payloads = this.payloads[namespace]
return payloads[payloads.length - n - 1]
},
[inject]: newState => saveState(state = newState)
}
// save the initial state
saveState(initialState)
// make it available
this.stores[namespace] = store
// listener to save state locally for history
store.subscribe(saveState)
return store
}
takeSnapshot() {
return this.state
}
bootstrap(serializedData) {
const data = this.deserialize(serializedData)
Object.keys(data).forEach((name) => {
this.stores[name][inject](data[name])
})
}
prepare(store, payload) {
return this.serialize({
[store.name]: payload
})
}
recycle() {
Object.keys(this.stores).forEach((name) => {
this.stores[name][inject](this.payloads[name][0])
})
}
flush() {
const snapshot = this.takeSnapshot()
this.recycle()
return snapshot
}
}
import assign from 'object-assign'
const alt = new Alt({ Dispatcher, history: 300 })
// The name is for
// A) to reference this later
// B) DispatcherRecorder and logging
const actions = alt.createActions('Actions', {
fire(x) {
return x
},
blah() { }
})
console.log('Actions =>', actions)
const store = alt.createStore('MyStore', (state, { action, data, meta }) => {
return assign({}, state, {
b: data
})
}, {
a: 0,
b: 0
})
console.log('Store =>', store)
console.log('Snapshot 1, 3', alt.takeSnapshot())
store.subscribe((state) => {
console.log('* Change Event:', state)
})
actions.fire(1)
actions.fire(2)
actions.fire(3)
actions.fire(4)
console.log('PEEKING 0, 1', store.peek(3))
const newState = store.peek(3) // 0, 1
const serialized = alt.prepare(store, newState) // MyStore: { a: 0, b: 1 }
alt.bootstrap(serialized)
console.log('Snapshot 0, 1', alt.takeSnapshot())
@threepointone
Copy link

threepointone commented May 6, 2015

neat!

@goatslacker
Copy link
Author

goatslacker commented May 7, 2015

The only thing missing from this is how to deal with derived data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment