Created
March 24, 2018 21:22
-
-
Save andrejewski/d90aeec907eb6cade6205ba3fa5548fd to your computer and use it in GitHub Desktop.
Another stab at declarative subscriptions in Raj
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
| /* | |
| 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