Ramonda

Subscriptions

Some code has to reach outside your component: subscribe to a store, open a socket, observe an element. It needs three things — to run once the component is on the page, to be torn down when the component goes, and to be redone if what it subscribed to changes.

That shape has one name here. You declare a decorator once, saying how to connect, and what it returns is the cleanup:

import { createSubscriptionDecorator } from "@ramonda/core";

export const onStore = createSubscriptionDecorator(
  "onStore",
  (_owner, handler: (state: ThemeState) => void, store: ThemeStore) => store.subscribe(handler),
);
export class Panel extends Component {
  @state theme = "light";

  @onStore(themeStore)
  themeChanged(next: ThemeState) {
    this.theme = next.theme;
  }
}

connect runs after the DOM is committed. Whatever it returns is called before it runs again, and once more when the component is destroyed — so a connection opened here is always closed. Full detail, including how to demand something of the class it goes on, is in writing your own decorators.

It re-runs when a signal it READ changes. Read owner.channel inside connect and switching channels disconnects the old one before opening the new — which is the whole reason this is not just "subscribe in @mounted, unsubscribe in @destroyed".

    The built-in ones

    Several decorators are this same machinery with the connect already written, which is why none of them need a cleanup from you:

    @onElement / @onWindow / @onDocumenta DOM listener, removed on destroy
    @interval / @timeouta timer, cleared on destroy
    @deferHydrationwaits for a promise before hydrating

    Not everything outside is a subscription

    Three other things are reached for at the same moment, and each has a name of its own — which is the point, because the name says when it runs:

    After the render, every time — measuring an element, scrolling something into view: @updated. It runs once the commit is done, so the DOM is the one you are looking at.

    When a prop changes — refetching for a new id, resetting a form: @watchProp. It runs before the render, so what it derives is on screen in the same pass rather than one frame later.

    Deriving a value@compute. If the answer is a value, return it; nothing needs to run and nothing needs to be cleaned up.

    One name per purpose

    Each of the four says what it is FOR, and that is what lets the framework know when to run it, in what order, and what to say when it goes wrong.

    The alternative is one mechanism keyed on what a body happens to READ, and it costs both of those. Which of the four things it was doing would change from render to render with the reads, so there is no order to guarantee and nothing specific to report. And two such bodies, each writing what the other reads, re-trigger each other — the one way to hang a page that no diagnostic can explain in a sentence.

    Looking for one of these? The name is on the left:

    what you wanthere
    subscribe to something outside, clean up on unmountyour own decorator
    read or correct the DOM after it changed@updated
    react to a prop changing@watchProp
    derive a value from other values@compute
    fetch data for a key@ramonda/query

    Next