Core concepts
Communication
miso provides four mechanisms for components to exchange data.
- Props — synchronous, parent-to-child read-only data passed at mount time; see Props.
- Context — global data shared by the entire tree; any component can mutate it via
modifyContextand opt in to re-renders withuseContext; see Context. - Mailbox — message passing via
mail,broadcast,checkMail; any component can send a JSONValueto any other byComponentId. - PubSub (
Miso.PubSub) — publish / subscribe for fan-out messaging across unrelated components.
The mailbox
Every component has a mailbox — a slot that receives Value messages sent by other components. Messages are dispatched asynchronously via the event queue.
Sending
mail componentId msg- send to a specific
ComponentId(obtained viaaskinsideEffect) mailParent msg- send to the direct parent
mailChildren msg- send to all immediate children
mailAncestors msg- walk up the hierarchy, delivering to every ancestor
mailDescendants msg- walk down the hierarchy, delivering to every descendant
broadcast msg- deliver to every mounted component except the sender
Receiving with checkMail
Wire up the mailbox field with checkMail, which handles JSON parsing and routes to success / error actions:
data Action
= ReceivedMsg MyMsg
| MailError MisoString
myComp :: Component context props model Action
myComp = (vcomp m u v) { mailbox = checkMail ReceivedMsg MailError }Looking up a ComponentId
update = \case
SendMsg targetId -> io_ (mail targetId ("hello" :: MisoString))
GetMyId -> do
info <- ask
let myId = _componentInfoId info
...PubSub
Miso.PubSub provides topics: a component subscribes to a topic (receiving messages as actions) and any component may publish to it. It is the right tool when the sender does not know who is listening.
notifications :: Topic Note -- a typed topic; Note has ToJSON / FromJSON
notifications = topic "notifications"
update = \case
Init -> subscribe notifications Notified NotifyError
Notify n -> io_ (publish notifications n)
Notified n -> ... -- n :: Note
NotifyError _ -> pure ()Try it
-- Two siblings that do not know each other talk over a PubSub topic; the
-- publisher also mails its parent, which relays the message to every child.
data Note = Note MisoString
deriving (Show, Eq, Generic, ToJSON, FromJSON)
notes :: Topic Note
notes = topic "demo-notes"
data ChatAction = Relayed Note | MailErr MisoString
chat :: Eq ctx => Component ctx () () ChatAction
chat = (component () update view)
{ mailbox = checkMail Relayed MailErr }
where
update = \case
Relayed n -> mailChildren n -- parent → all of its children
MailErr _ -> pure ()
view _ () _ =
H.div_ [ HP.class_ "cols" ]
[ "publisher" +> publisher
, "subscriber" +> subscriber
]
data PubAction = Send | Draft MisoString
publisher :: Component ctx () MisoString PubAction
publisher = component "hello from the publisher" update view
where
update = \case
Draft s -> this .= s
Send -> do
s <- get
io_ (publish notes (Note s)) -- fan out over the topic (IO)
mailParent (Note s) -- and tell the parent directly
view _ () s =
H.div_ []
[ H.input_ [ HP.value_ s, HE.onInput Draft ]
, H.button_ [ HE.onClick Send ] [ "publish" ]
]
data SubAction = Subscribe | Got Note | Oops MisoString
subscriber :: Component ctx () [MisoString] SubAction
subscriber = (component [] update view)
{ mount = Just Subscribe -- subscribe on mount
, mailbox = checkMail Got Oops } -- also accepts parent mail
where
update = \case
Subscribe -> subscribe notes Got Oops
Got (Note s) -> this %= take 5 . (s :)
Oops _ -> pure ()
view _ () received =
H.ul_ [ HP.class_ "log" ] [ H.li_ [] [ text s ] | s <- received ]The publisher does two things on send: it publishes to a topic the subscriber joined on mount, and it mailParents the parent, which relays with mailChildren — so each message arrives at the subscriber twice, once by each route.