Core concepts
Context
context is miso's analogue of React Context: a single, global value shared by every component in the tree, without threading it through props at each level. This very site keeps its language table and colour theme there.
Contrast the three pieces of state a component sees, by scope:
- model — private to a single component.
- props — passed from a parent to its immediate child.
- context — global; the same value is visible to the whole tree.
This is why context is a type parameter on both Component and View: it is threaded through the entire tree so that every nested component — reachable via SomeComponent — is statically guaranteed to agree on one context type. There is exactly one live context value per application.
Seeding
startAppWithContext- the client entry point, replaces
startApp. misoWithContext / prerenderWithContext- the hydrating counterparts of
miso/prerender. setContext- seeds the value directly. Needed for server-side rendering, where a
Viewis serialised without starting the runtime. liveWithContext / reloadWithContext- context-aware variants of
live/reloadfor interactive (GHCi) development.
Reading
The current context is the first argument of every component's view, so any component — however deeply nested — reads it synchronously during render:
view :: context -> props -> model -> View context model action
view ctx _props _model = ...Inside update it is readable in the Effect monad, just like props — use getContext (or Miso.Lens.view with the context lens):
update Toggle = do
ctx <- getContext
...Updating
Mutate the context with modifyContext (or putContext to replace it):
update Toggle = modifyContext (\theme -> if theme == Light then Dark else Light)Re-rendering on change
When the context value changes (per its Eq instance), every component with useContext = True is re-rendered against the new value. useContext defaults to False, so components opt in:
child = (component m u v) { useContext = True }Try it
The context says: theme = Light, language = English
useContext = False: I still think the theme is Light
-- This site's context holds the language and the theme. Any component can
-- read it (first argument of view) and change it (modifyContext). Only
-- components with useContext = True re-render when it changes.
data ThemeAction = FlipTheme
themeSwitch :: Component Ctx () () ThemeAction
themeSwitch = (component () update view)
{ useContext = True }
where
update FlipTheme = do
ctx <- getContext -- context is readable in Effect
let theme = if ctxTheme ctx == Dark then Light else Dark
modifyContext (\c -> c { ctxTheme = theme })
io_ $ do -- persist + apply, like the top bar
setLocalStorage "miso.theme" (themeCode theme)
html <- jsg "document" ! "documentElement"
void (html # "setAttribute" $ ("data-theme" :: MisoString, themeCode theme))
view ctx () () =
H.div_ []
[ H.p_ []
[ "The context says: theme = ", H.strong_ [] [ text (ms (show (ctxTheme ctx))) ]
, ", language = ", H.strong_ [] [ text (langName (ctxLang ctx)) ]
]
, H.button_ [ HE.onClick FlipTheme ] [ "Flip the whole site's theme" ]
, "frozen" +> frozen
]
-- A sibling that does not opt in: it keeps showing the context it mounted with.
frozen :: Component Ctx () () ()
frozen = component () (\() -> pure ()) view -- useContext defaults to False
where
view ctx () () =
H.p_ [ HP.class_ "muted" ]
[ "useContext = False: I still think the theme is "
, text (ms (show (ctxTheme ctx))) ]The first component opts in with useContext = True and re-renders on every change; the second does not, so it keeps showing whatever the context was when it mounted. The button really does change the site's theme — the top bar's toggle and this demo share one value.
Example: this website
The site's context is a record with the active language, a translation table and the theme. The top bar's dropdown calls modifyContext; every page component has useContext = True and renders text nodes by looking keys up in the table:
data Ctx = Ctx { ctxLang :: Lang, ctxCatalog :: Catalog, ctxTheme :: Theme }
deriving Eq
t :: Ctx -> Key -> View Ctx model action
t ctx key = text (translate ctx key)
update (SetLang l) = do
modifyContext (\ctx -> ctx { ctxLang = l })
io_ (setLocalStorage "miso.lang" (langCode l))