15 min/d

ぼうずやのにっき

Halogen の Component を今度こそ String にする話

何度か書いてきた。 slamdata/purescript-halogen の Component を String にする話。過去の記事は↓あたり。

Halogen の Component を String にしたい。 server side rendering する際に Component をそのまま使えると (実行効率はともかく) 何度も書かなくて済む。こんなことくらい当然のように書いてあると思いきや、情報がなくていろいろ模索した。さんざん苦労したのに Component をバラす方法に変えたら、あっという間に動いてしまった。

細かい説明は面倒なので、ソースコードを示す。

https://github.com/bouzuya/tamaru/blob/f2692c82ffd295958f8a5212b0c0f035435af719/src/Bouzuya/Halogen/StringRenderer.purs

module Bouzuya.Halogen.StringRenderer (render) where

import Halogen as H
import Halogen.HTML as HH
import Halogen.VDom.DOM.StringRenderer as VSR
import Prelude (Unit)

render
  :: forall f i o m
  . H.Component HH.HTML f i o m
  -> i
  -> String
render = componentToString

componentToString
  :: forall f i o m
  . H.Component HH.HTML f i o m
  -> i
  -> String
componentToString component input =
  H.unComponent
    (\{ initialState, render } ->
      let (HH.HTML vdom) = render (initialState input) in
      VSR.render componentSlotToString vdom
    )
    component

componentSlotToString
  :: forall f g m p
  . H.ComponentSlot HH.HTML g m p (f Unit)
  -> String
componentSlotToString slot =
  H.unComponentSlot
    (\_ component input _ _ _ -> componentToString component input)
    slot

雑に言うと unComponentunComponentSlot を使って Component やその子である ComponentSlot をバラして、あとは Halogen.VDom.DOM.StringRendererrender に食わせる。バラしたものは initialStaterender そして input くらいしか使わない。 initialStateInput -> StaterenderState -> HTML ...inputInput 。あとはその組み合わせ。

苦労が嘘みたいに簡単だった。まだ何か漏れていそうだけど。