Last active
June 3, 2018 18:55
-
-
Save layflags/b33fe55bbe1099babb1554ae79c49d5e to your computer and use it in GitHub Desktop.
Port of the reactor bundle (part of HenrikJoreteg/redux-bundler) for HyperApp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import createDebug from 'debug'; | |
/* eslint-disable no-restricted-globals */ | |
const HAS_WINDOW = typeof window !== 'undefined'; | |
const IS_BROWSER = HAS_WINDOW || typeof self !== 'undefined'; | |
const ricOptions = { timeout: 500 }; | |
// helpers | |
const debug = createDebug('hyperapp-reactor'); | |
const fallback = func => { | |
setTimeout(func, 0); | |
}; | |
const raf = | |
IS_BROWSER && self.requestAnimationFrame | |
? self.requestAnimationFrame | |
: fallback; | |
const ric = | |
IS_BROWSER && self.requestIdleCallback ? self.requestIdleCallback : fallback; | |
const isArray = | |
Array.isArray || (arr => ({}.toString.call(arr) === '[object Array]')); | |
const values = obj => Object.keys(obj).map(key => obj[key]); | |
const debounce = (fn, wait) => { | |
let timeout; | |
function debounced(...args) { | |
const ctx = this; | |
clearTimeout(timeout); | |
timeout = setTimeout(() => { | |
fn.apply(ctx, args); | |
}, wait); | |
} | |
debounced.cancel = () => { | |
clearTimeout(timeout); | |
}; | |
return debounced; | |
}; | |
const getIdleDispatcher = (stopWhenInactive, timeout, fn) => | |
debounce(() => { | |
// the requestAnimationFrame ensures it doesn't run when tab isn't active | |
if (stopWhenInactive) { | |
raf(() => ric(fn, ricOptions)); | |
} else { | |
ric(fn, ricOptions); | |
} | |
}, timeout); | |
// HOA | |
const withReactions = ( | |
reactorListOrMap, | |
{ | |
idleTimeout = 30000, | |
idleCallback = Function.prototype, | |
doneCallback = Function.prototype, | |
stopWhenTabInactive = true, | |
} = {} | |
) => { | |
if (!reactorListOrMap) throw Error('reactors missing'); | |
// internal idle action increments `idleCount` | |
// and calls `idleCallback` if defined | |
const idle = () => _state => { | |
const idleCount = _state.idleCount + 1; | |
debug(`app idled (${idleCount})`); | |
idleCallback(_state); | |
return { idleCount }; | |
}; | |
return app => (state, actions, view, container) => { | |
let executeNextReaction; | |
let idleDispatcher; | |
let cancelIfDone; | |
let nextReaction; | |
let activeReactor; | |
// create the app | |
const boundActions = app( | |
{ idleCount: 0, ...state }, | |
{ | |
idle, | |
...actions, | |
}, | |
(_state, _actions) => { | |
executeNextReaction(_state); | |
if (idleDispatcher) { | |
idleDispatcher(); | |
cancelIfDone(); | |
} | |
return view(_state, _actions); | |
}, | |
container | |
); | |
// create idle dispatcher | |
if (idleTimeout) { | |
idleDispatcher = getIdleDispatcher(stopWhenTabInactive, idleTimeout, () => | |
boundActions.idle() | |
); | |
} | |
cancelIfDone = () => { | |
if (!IS_BROWSER && !nextReaction) { | |
if (idleDispatcher) idleDispatcher.cancel(); | |
debug('idle dispatcher canceled'); | |
doneCallback(); | |
} | |
}; | |
const reactors = isArray(reactorListOrMap) | |
? reactorListOrMap | |
: values(reactorListOrMap); | |
const boundReactors = reactors.map(reactor => { | |
const boundReactor = reactor(boundActions); | |
boundReactor.displayName = reactor.name || 'anonymous'; | |
return boundReactor; | |
}); | |
executeNextReaction = _state => { | |
// one at a time | |
if (nextReaction) { | |
debug(`reactor '${activeReactor}' skipped`); | |
return; | |
} | |
// look for the next one | |
boundReactors.some(reactor => { | |
const result = reactor(_state); | |
if (result) { | |
activeReactor = reactor.displayName; | |
nextReaction = result; | |
return true; | |
} | |
return false; | |
}); | |
if (nextReaction) { | |
// let browser chill | |
ric(() => { | |
nextReaction(); | |
debug(`reactor '${activeReactor}' called`); | |
nextReaction = null; | |
activeReactor = null; | |
}, ricOptions); | |
} | |
}; | |
return boundActions; | |
}; | |
}; | |
export default withReactions; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Still WIP