Skip to content

Instantly share code, notes, and snippets.

@andrejewski
Created March 24, 2018 21:22
Show Gist options
  • Select an option

  • Save andrejewski/d90aeec907eb6cade6205ba3fa5548fd to your computer and use it in GitHub Desktop.

Select an option

Save andrejewski/d90aeec907eb6cade6205ba3fa5548fd to your computer and use it in GitHub Desktop.
Another stab at declarative subscriptions in Raj
/*
Another day spent on declarative subscriptions.
This implementation works like this:
subscription({
program: myProgram,
subscription: {
foo: sub1,
bar: sub2
},
subscribe: model => ({
foo: model.isListeningForFoo && Msg.SetFoo,
bar: Msg.SetBar
})
})
Advantages:
- Declarative. foo and bar are effectively managed
based on subscribe() having a callback for them.
- Conventional. Using the existing subscription
interface of {effect,cancel} to manage.
- No changes to the runtime, just another high
order program (a simple one at that).
Disadvantages:
- Inflexible. Subscriptions and callbacks must be
defined before the program begins.
- No model-parameterized subscriptions.
- No way to have an unbounded set of subscriptions.
This is probably the most ergonomic solution yet.
I may actually use this myself, but the inflexibility
makes it something I hesistate to market as the
"subscription" solution.
*/
import { mapEffect, batchEffects } from 'raj-compose'
function diff (prev, next) {
const isRemoval = !!(previousSub && !nextSub)
if (isRemoval) {
return { isRemoval: true }
}
const isAddition = !!(!previousSub && nextSub)
if (isAddition) {
return { isAddition: true }
}
const isChange = previousSub !== nextSub
if (isChange) {
return { isRemoval: true, isAddition: true }
}
return {}
}
function transition (subscriptions, previous, next) {
const effects = []
for (const key in subscriptions) {
const subscription = subscriptions[key]
const nextSub = next[key]
const { isRemoval, isAddition } = diff(previous[key], nextSub)
if (isRemoval) {
effects.push(() => subscription.cancel())
}
if (isAddition) {
effects.push(mapEffect(subscription.effect, nextSub))
}
}
return batchEffects(effects)
}
function subscription ({ program, subscriptions, subscribe }) {
const [programModel, programEffect] = program.init
const subscriptionCallbacks = subscribe(programModel)
const subscriptionEffect = transition(
subscriptions,
{},
subscriptionCallbacks
)
const init = [programModel, batchEffects([subscriptionEffect, programEffect])]
function update (msg, model) {
const [newModel, effect] = program.update(msg, model)
const oldSubscriptions = subscribe(model)
const newSubscriptions = subscribe(model)
const subscriptionEffect = transition(
subscriptions,
oldSubscriptions,
newSubscriptions
)
return [newModel, batchEffects([effect, subscriptionEffect])]
}
function done (model) {
if (program.done) {
program.done(model)
}
transition(subscriptions, subscribe(model), {})()
}
return { init, update, done, view: program.view }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment