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.
toHtmlWith
supplies the context (with the props and model) when serialising a View without starting the runtime — the server-side rendering path. As of 1.14.0 there is no global context cell to seed: setContext is gone, the value is passed to the renderer.
liveWithContext / reloadWithContext
context-aware variants of live / reload for interactive (GHCi) development.

Reading

As of 1.14.0 a view takes only the model. The context is read ambiently with vcontext, so any component — however deeply nested — still reads it synchronously during render, without anything threading it down (see Ambient accessors):

view
  :: model
  -> View context props model action
view _model =
  vcontext $ \ctx -> ...

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

Reading and changing the site's contextlive

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 (ambiently, with vcontext) 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 () =
      -- context is ambient in the view: read it with 'vcontext'
      vcontext $ \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 () =
      vcontext $ \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 props model action
t ctx key = text (translate ctx key)

update (SetLang l) = do
  modifyContext (\ctx -> ctx { ctxLang = l })
  io_ (setLocalStorage "miso.lang" (langCode l))