メインコンテンツまでスキップ

Halogenの基本

· 約8分

HalogenPureScript製のWeb UIフレームワークです。

個人的にはReactのPureScript版みたいなイメージです。Halogenにもコンポーネントという概念がありますが、多少の違いに目を瞑れば、ほぼReactのコンポーネントと一緒の概念です。 Halogenでの開発は、Reactと同様にコンポーネントを作成し、コンポーネントを組み合わせてウェブページを作っていきます。なのでHalogenでもコンポーネントを理解することが重要になります。

Halogenのコンポーネント

Halogenコンポーネントはざっくりまとめると以下の2つの機能を持っています。

Halogenのコンポーネントの機能
  • HTMLタグを書いてレンダリング
  • 状態の管理

以下で上記の機能について詳しく見ていきましょう!

コンポーネントの作成

コンポーネントを作成するには以下の3つの関数を定義する必要があります。

H.mkComponent
{ initialState: ...
, render: ...
, eval: ...
}
関数名説明
initialState状態のデフォルト値を設定する関数。
renderHTMLをレンダリングする関数。
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)を返すだけです。evalActionを受け取り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
, ...
}
}

handleActionActionを処理する関数です。副作用を扱えます。下記の例では副作用にMonadAffを使っています。

handleAction :: forall output m. MonadAff m => Action -> H.HalogenM State Action () output m Unit

ActionhandleActionで行う処理の名前のようなものです。副作用が必要な初期化処理や終了処理が必要な場合も、Actionとして定義し、eval関数のinitializefinalizeに渡します。(上記参照)

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.liftEffectEffectをHalogenMに変換するres <- Halogen.liftEffect doSomethingEffect
Halogen.liftAffAffをHalogenMに変換するres <- Halogen.liftAff doSomethingAff
Halogen.Subscription.createemitter, listenerを生成する{ emitter, listener } <- Halogen.liftEffect HalogenSubscription.create
Halogen.Subscription.notifyActionを送るHalogen.Subscription.notify listener SomeAction
Halogen.subscribeサブスクする_ <- H.subscribe emitter
pureHalogenMを生成する pure unit