Re-derive subscriptions after every update instead of only at init#514
Merged
DROOdotFOO merged 3 commits intoJul 13, 2026
Merged
Conversation
`setup_subscriptions/1` ran once, from `init/1`, against the initial model.
Subscriptions were therefore frozen: an app whose `subscribe/1` depends on
state -- poll only while a job runs, tick only the currently-selected view --
declared them, and the runtime never asked again, so they were never started.
The playground is the visible case. `Playground.App.subscribe/1` returns the
selected demo's subscriptions, but at mount the selection is the first widget
(no ticks), so every tick-driven demo -- the scroll-anchor stream, easing,
sparkline -- was dead on arrival.
Subscriptions are a function of the model, so derive them from it: after every
update, re-run `subscribe/1`, diff against what is running, start what is newly
declared and stop what is no longer declared. A `%Subscription{type, data}` is
its own identity, so an unchanged subscription keeps its existing timer rather
than being torn down and restarted on every keystroke.
2 tasks
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Dispatcher.setup_subscriptions/1was called once, frominit/1, against the initial model — and never again (two grep hits: the call site and the definition). Subscriptions were effectively static.That breaks any app whose
subscribe/1depends on state, which is the normal TEA pattern: poll only while a job is running, tick only the currently-selected view. The app declares the subscription; the runtime never asks again; it is never started.The visible case is the playground.
Playground.App.subscribe/1returns the selected demo's subscriptions, but at mount the selection is the first widget (which has no ticks). So every tick-driven demo — the scroll-anchor stream, Easing, Sparkline — was dead on arrival. Selecting them changedsubscribe/1's result, but nothing re-read it.Fix: subscriptions are a function of the model, so derive them from it. After every update, re-run
subscribe/1, diff against the running set, start what's newly declared, stop what's no longer declared. A%Subscription{type, data}is its own identity, so an unchanged subscription keeps its existing timer rather than being torn down and restarted on every keystroke — no churn.This is Elm's
subscriptionssemantics, which Raxol had declared but not implemented.New tests (
dispatcher_subscriptions_test.exs): a sub declared only by the updated model gets started; a sub no longer declared gets stopped; an unchanged sub keeps the same subscription id.Full suite: 4691/4697. The 6 failures are pre-existing and environmental (5 × live-Postgres roundtrip needing a DB, 1 × terminal-type detection) — untouched by this change.