HalogenはPureScript製のWeb UIフレームワークです。
個人的にはReactのPureScript版みたいなイメージです。Halogenにもコンポーネントという概念がありますが、多少の違いに目を瞑れば、ほぼReactのコンポーネントと一緒の概念です。 Halogenでの開発は、Reactと同様にコンポーネントを作成し、コンポーネントを組み合わせてウェブページを作っていきます。なのでHalogenでもコンポーネントを理解することが重要になります。
Halogenのコンポーネント
Halogenのコンポーネントはざっくりまとめると以下の2つの機能を持っています。
- HTMLタグを書いてレンダリング
- 状態の管理
以下で上記の機能について詳しく見ていきましょう!
コンポーネントの作成
コンポーネントを作成するには以下の3つの関数を定義する必要があります。
H.mkComponent
{ initialState: ...
, render: ...
, eval: ...
}
関数名 | 説明 |
---|---|
initialState | 状態のデフォルト値を設定する関数。 |
render | HTMLをレンダリングする関数。 |
eval | 副作用のある処理を行う関数。 |
HalogenのHTMLタグのレンダリング
HalogenではReactのようにコンポーネント内でHTMLタグを書きますが、JSXのようなものはありません。PureScriptの文法の範囲内で記述することになります。具体的には以下のようなイメージです。
-- 公式ドキュメントより引用
HH.div
[ HP.id "root" ]
[ HH.input
[ HP.placeholder "Name" ]
, HH.button
[ HP.classes [ HH.ClassName "btn-primary" ]
, HP.type_ HP.ButtonSubmit
]
[ HH.text "Submit" ]
]
これを通常のHTMLで表現すると以下のようになります。
<!-- 公式ドキュメントより引用 -->
<div id="root">
<input placeholder="Name" />
<button class="btn-primary" type="submit">
Submit
</button>
</div>
このHTMLタグをコンポーネントのrender関数内に定義します。render関数は以下のようなシグネチャです。
render :: forall m. State -> H.ComponentHTML Action () m
ざっくりいうと状態を受け取ってHTMLを返す関数です。
render関数に副作用(EffectやAffなど)を含めることはできません。副作用の処理はコンポーネントのeval関数内で行います。
Halogenの状態管理
Halogenの状態管理は以下のことができます。
処理 | 対応する関数 |
---|---|
状態のデフォルト値の設定 | initialState |
Actionの処理 | handleAction |
状態の初期化 | initialize |
状態の終了処理 | finalize |
サブスクリプション | handleAction |
Queryの処理 | handleQuery |
Inputの処理 | receive |
上記の処理を全てeval関数内で行います。
HTMLのonClickなどのイベントハンドラでHTTPリクエストなどの副作用を伴う処理は実行できません。イベントハンドラではevalで行う処理名(Action)を返すだけです。evalはActionを受け取りActionの値にしたがって副作用のある処理を実行します。
公式ドキュメントの例を覗いてみましょう。
Actionの定義:
data Action = MakeRequest Event
onSubmitの定義:
HH.form
[ HE.onSubmit \ev -> MakeRequest ev ]
eval関数のActionを処理する部分:
handleAction = case _ of -- Actionを受け取る
MakeRequest event -> do
-- HTTPリクエストなどの副作用を伴う処理
response <- getSomethingWithEffect
-- 状態を更新する
H.put $ updateStateByResponse response
以下でコンポーネントが持つ状態管理機能について詳しく見ていきましょう。
状態のデフォルト値
コンポーネントには任意の状態を設定できます。コンポーネント作成時にその状態のデフォルト値をinitialState関数で指定する必要があります。
状態の定義の例:
type State = {
userName :: String
}
component =
H.mkComponent
{ initialState: \_ -> { userName: "Hoge" }
, render: ...
, eval: ...
}
状態の更新
状態の更新はコンポーネントのeval関数の中のhandleActionで行います。
H.mkComponent
{ initialState: ...
, render: ...
, eval: H.mkEval $ H.defaultEval
{ handleAction = handleAction
, ...
}
}
handleAction はActionを処理する関数です。副作用を扱えます。下記の例では副作用にMonadAffを使っています。
handleAction :: forall output m. MonadAff m => Action -> H.HalogenM State Action () output m Unit
Action はhandleActionで行う処理の名前のようなものです。副作用が必要な初期化処理や終了処理が必要な場合も、Actionとして定義し、eval関数のinitialize、finalizeに渡します。(上記参照)
data Action = Initialize
| Finalize
| UpdateUserName String
handleAction は、クリックイベントなどからActionを受け取りそれを処理し、HalogenMモナドを返す関数です。
handleAction = case _ of
Initialize -> ...
Finalize -> ...
UpdateUserName userName -> H.put { userName }
サブスク
イベントをサブスクして状態を更新するため関数も用意されています。以下の例ではonUserNameChangedというuserNameが変更されたときに呼ばれる関数をサブスクしています。
onUserNameChanged関数内でUpdateUserNameアクションを発行している点に注意してください。
handleAction = case _ of
Initialize -> do
{ emitter, listener } <- H.liftEffect HS.create
H.liftEffect $ onUserNameChanged $ \userName -> HS.notify listener (UpdateUserName username)
_ <- H.subscribe emitter
UpdateUserName userName -> H.put { userName }
handleActionで使える便利な関数
最後にhandleActionで使える便利な関数を整理します。
関数名 | 説明 | 例 |
---|---|---|
Halogen.get | 状態を取得する | state <- Halogen.get |
Halogen.put | 状態を更新する | Halogen.put state |
Halogen.raise | 親にデータを送る | Halogen.raise output |
Halogen.liftEffect | EffectをHalogenMに変換する | res <- Halogen.liftEffect doSomethingEffect |
Halogen.liftAff | AffをHalogenMに変換する | res <- Halogen.liftAff doSomethingAff |
Halogen.Subscription.create | emitter, listenerを生成する | { emitter, listener } <- Halogen.liftEffect HalogenSubscription.create |
Halogen.Subscription.notify | Actionを送る | Halogen.Subscription.notify listener SomeAction |
Halogen.subscribe | サブスクする | _ <- H.subscribe emitter |
pure | HalogenMを生成する | pure unit |