Skip to content

Instantly share code, notes, and snippets.

@yelouafi
Last active May 16, 2016 09:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yelouafi/9a6302d90adec6e54dc14797c9af9ace to your computer and use it in GitHub Desktop.
Save yelouafi/9a6302d90adec6e54dc14797c9af9ace to your computer and use it in GitHub Desktop.
Observable state tree
<!DOCTYPE html>
<html>
<head>
<title>Redux basic example</title>
<script src="https://npmcdn.com/redux@latest/dist/redux.min.js"></script>
<script src="redux-dirty.js" charset="utf-8"></script>
</head>
<body>
<script>
const field = (name, seed) => (state=seed, action) => {
if(action.type === 'SET_FIELD' && action.field === name)
return action.value
return state
}
const store = createObservableStore(Redux.createStore)(combineWithObs({
name: field('name', 'yassine'),
age: field('age', 38),
address: {
street: field('street', 'Rue Massinet'),
city: field('city', 'tanger')
}
}))
</script>
</body>
</html>
function remove(array, item) {
const index = array.indexOf(item)
if(index >= 0) {
array.splice(index, 1)
}
}
function emitter() {
const subscribers = []
let dirty
let state
let initialized
function subscribe(sub) {
subscribers.push(sub)
return () => remove(subscribers, sub)
}
function notify() {
if(!dirty) return
for (var i = 0, len = subscribers.length; i < len; i++) {
subscribers[i](state)
}
dirty = false
}
function setState(newState) {
if(!initialized) {
initialized = true
state = newState
} else if(state !== newState) {
state = newState
dirty = true
}
}
return {
subscribe,
notify,
getState: () => state,
setState
}
}
function makeObs(reducer, key) {
const em = emitter()
function obsReducer(state, action) {
em.setState( reducer(state, action) )
return em.getState()
}
obsReducer.name = em.name = key
obsReducer.emitter = em
return obsReducer
}
function combineWithObs(obj, key) {
const keys = Object.keys(obj)
const em = emitter()
em.$$children = []
const childReducers = {}
keys.forEach(key => {
const ch = obj[key]
const chRed = typeof ch === 'function' ? makeObs(ch, key) : combineWithObs(ch, key)
childReducers[key] = chRed
em[key] = chRed.emitter
em.$$children.push(chRed.emitter)
})
function combinedObs(state, action) {
let newState = state === undefined ? {} : undefined
for (var i = 0, len = keys.length; i < len; i++) {
updateChildState(keys[i], action)
}
if(!newState) {
return state
} else {
for (var i = 0, len = keys.length; i < len; i++) {
const key = keys[i]
if(!newState.hasOwnProperty(key)) {
newState[key] = state[key]
}
}
em.setState(newState)
return newState
}
function updateChildState(key, action) {
const oldChState = state !== undefined ? state[key] : undefined
const newChState = childReducers[key](oldChState, action)
if(state === undefined || oldChState !== newChState) {
if(!newState) newState = {}
newState[key] = newChState
}
}
}
combinedObs.emitter = em
return combinedObs
}
function createObservableStore(next) {
let rootEmitter
return function createStoreWithObs(reducer, initialState) {
const store = next(reducer, initialState)
rootEmitter = reducer.emitter
if(!rootEmitter) {
return store
} else {
return Object.assign({}, store, {
$: rootEmitter,
dispatch(action) {
const res = store.dispatch(action)
notify(rootEmitter)
return res
}
})
}
}
function notify(em) {
em.notify()
const children = em.$$children
if(children) {
for (var i = 0, len = children.length; i < len; i++) {
notify(children[i])
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment