大家好。今天和大家一起看 Introducing Components。
昨天寫完 halogen html,今天終於開始 component 了。一開始就來個 example:
module Main where
import Prelude
import Halogen as H
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
data Action = Increment | Decrement
component =
H.mkComponent
{ initialState
, render
, eval: H.mkEval H.defaultEval { handleAction = handleAction }
}
where
initialState _ = 0
render state =
HH.div_
[ HH.button [ HE.onClick \_ -> Decrement ] [ HH.text "-" ]
, HH.text (show state)
, HH.button [ HE.onClick \_ -> Increment ] [ HH.text "+" ]
]
handleAction = case _ of
Decrement ->
H.modify_ \state -> state - 1
Increment ->
H.modify_ \state -> state + 1
可以看出幾個東西:
eval
裡面提供 handleAction
function 處理,裡面似乎可以做 side effect。接下來 tutorial 嘗試一步步的把這個 component 分拆,再把該有的 type 加回去。
type State = Int
這種使用了 Type Synonyms 把 component state 定義為 Int
。這個使用方法跟 typescript 的 type
keyword 是一樣的。
接下來是定義 Action:
data Action = Increment | Decrement
這個 data
keyword 可能就有點陌生了。data
實際上是所謂的 algebraic data types (ADTs),在 typescript 裡面就是所謂的 tagged unions。(發現 purescript 好像也改叫 tagged unions 了⋯) 如果用 Typescript 去定義 Action 的話大槪會長這樣:
enum ActionType {
Increment,
Decrement,
}
type Action = IncrementAction | DecrementAction
type IncrementAction = {
type: ActionType.Increment,
}
type DecrementAction = {
type: ActionType.Decrement,
}
在介紹 handleAction
的 type signature 時有提到還有 input、output 跟 child components 的 type parameters,似乎在以後會再介紹。
接下來介紹 render 跟 event handling,這部份我覺得蠻直觀的,就不詳述了。
比較有趣的地方是 Component 有分外部跟內部的 type definition,而 mkComponent
會幫忙生外部的 type definition,蠻有趣的。而 public component type 包括 effect monad 的,這是我比較感興趣的地方,希望之後會有詳細的說明。
最後是分拆好的 component!
module Main where
import Prelude
import Effect (Effect)
import Halogen as H
import Halogen.Aff as HA
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
import Halogen.VDom.Driver (runUI)
main :: Effect Unit
main = HA.runHalogenAff do
body <- HA.awaitBody
runUI component unit body
type State = Int
data Action = Increment | Decrement
component :: forall query input output m. H.Component query input output m
component =
H.mkComponent
{ initialState
, render
, eval: H.mkEval H.defaultEval { handleAction = handleAction }
}
initialState :: forall input. input -> State
initialState _ = 0
render :: forall m. State -> H.ComponentHTML Action () m
render state =
HH.div_
[ HH.button [ HE.onClick \_ -> Decrement ] [ HH.text "-" ]
, HH.text (show state)
, HH.button [ HE.onClick \_ -> Increment ] [ HH.text "+" ]
]
handleAction :: forall output m. Action -> H.HalogenM State Action () output m Unit
handleAction = case _ of
Decrement ->
H.modify_ \state -> state - 1
Increment ->
H.modify_ \state -> state + 1
到這裡今天就完成了。到這裡要寫些簡單的 component 應該是沒問題了。