Halogen Hook使用のbefore/after
Halogen Hooksの使用によって、Halogen Componentの書き方は大きく変わる
めちゃくちゃ簡単な例を用いてその比較をする
というか、今のmrsekut.iconには、従来のHalogen Componentで複雑な例を書くほどのHalogen力がないので
簡単すぎて、例としてあまり適切でない気はするmrsekut.icon
もっと複雑にした方が、Hookに移行した際の嬉しさがもっと増えるので。
冗長なのでimport文などは省略する
ただclickしたら、toggleするだけの例
この小さな例でもComponentは個別に状態を持つ必要がある
https://i.gyazo.com/da6e6ff88484a8173022922c1853968c.gif
2つにしたもの
https://i.gyazo.com/916a0198bb5ae75eb81431370ab3c170.gif
個別に状態が保持される
Before
従来のHalogen Componentの書き方
code:purs(hs)
type State =
{ flg :: Boolean
}
data Action
= Toggle
component :: ∀ m q. H.Component q Unit Void m
component =
H.mkComponent
{ initialState: const { flg: false }
, render
, eval: H.mkEval $ H.defaultEval
{ handleAction = handleAction
}
}
where
render :: State -> H.ComponentHTML Action () m
render {flg} = HH.div
onClick \_ -> Toggle
HH.text $ show flg
handleAction :: ∀ slot o
. Action
-> H.HalogenM State Action slot o m Unit
handleAction = case _ of
Toggle -> do
H.modify_ \{flg} -> { flg: not flg }
この例は小さすぎるので、これでも別にさほど大変ではないmrsekut.icon
例が悪い
mkComponentやmkEvalでごにょごにょ関数を書いてく必要がある
Actionや、必要であればSlotやOutputを定義して書く必要がある
同じような状態とその更新を持つものだとしても全く同じものを再度書く必要がある
code:purs(hs)
type State =
{ flg1 :: Boolean
, flg2 :: Boolean -- 追記
}
data Action
= Toggle1
| Toggle2 -- 追記
component :: ∀ m q. H.Component q Unit Void m
component =
H.mkComponent
{ initialState: const { flg1: false, flg2: false}
, render
, eval: H.mkEval $ H.defaultEval
{ handleAction = handleAction
}
}
where
render :: State -> H.ComponentHTML Action () m
render {flg1, flg2} = HH.div_ [
HH.div
onClick \_ -> Toggle1
HH.text $ show flg1,
HH.div
onClick \_ -> Toggle2
HH.text $ show flg2
]
handleAction :: ∀ slot o
. Action
-> H.HalogenM State Action slot o m Unit
handleAction = case _ of
Toggle1 -> do
H.modify_ \s -> s { flg1 = not s.flg1 }
Toggle2 -> do -- 追記
H.modify_ \s -> s { flg2 = not s.flg2 }
今回のは例が小さいので全く辛くはないが、でかいと複雑になる
また、この「clickでtoggleする」というロジックは、別のComponentで使いたい時も、上と同じようなコードをコピペする必要がある
つまり、上の例での(flg1, Toggle1)のような組のロジックを他のComponentで再利用できない
After
Halogen Hookを用いる
Custom Hookをこんな感じで定義する
code:purs(hs)
type UseToggle' = UseState Boolean <> Hooks.Pure
foreign import data UseToggle :: Hooks.HookType
instance HookNewtype UseToggle UseToggle'
useToggle :: ∀ m. Boolean -> Hook m UseToggle (Tuple Boolean (HookM _ Unit))
useToggle init = Hooks.wrap hook
where
hook :: Hook m UseToggle' _
hook = Hooks.do
flg /\ flgId <- Hooks.useState init
Hooks.pure $ flg /\ Hooks.modify_ flgId not
こんな感じでcomponent内で利用する
code:purs(hs)
component :: ∀ q i o m. MonadAff m => H.Component q i o m
component = Hooks.component \_ _ -> Hooks.do
flg /\ setFlg <- useToggle false
Hooks.pure do
HH.div
onClick \_ -> setFlg
HH.text $ show flg
component型でmkComponentなどを一切使っていない
React並に簡潔に書けていることがわかる
状態を複製するのも簡単
hookを2回呼べば良い
code:purs(hs)
component :: ∀ q i o m. MonadAff m => H.Component q i o m
component = Hooks.component \_ _ -> Hooks.do
flg1 /\ setFlg1 <- useToggle false
flg2 /\ setFlg2 <- useToggle false
Hooks.pure do
HH.div_ [
HH.div
onClick \_ -> setFlg1
HH.text $ show flg1,
HH.div
onClick \_ -> setFlg2
HH.text $ show flg2
]
別のcomponentでもこのuseToggleを呼べばよいだけなので、ロジックの再利用もできる
上の例ではuseToggleとしてcustom hookに切り出しているが、別にcomponent内に直接書いても良い
code:purs(hs)
component :: ∀ q i o m. MonadAff m => H.Component q i o m
component = Hooks.component \_ _ -> Hooks.do
flg /\ flgId <- Hooks.useState false
let setFlg = Hooks.modify_ flgId not
Hooks.pure do
HH.div_ [
HH.div
onClick \_ -> setFlg
HH.text $ show flg
]
以上はめちゃくちゃ簡単な例ではあるが、明らかにHalogen Hookを使ったほうが楽
学習コストの高い従来のHalogen Componentを学ぶ必要はない
しかし、既存のドキュメントや、その辺に転がっているHalogenの例が従来の書き方をしているものが多いので、学んでおいたほうが良い気もする..mrsekut.icon
上の例では、componentのネストなどをしていないが、ネストがあると従来のはより煩雑になっていく
結果、「あまりバンバンComponentに切り出さないほうが良い」みたいな運用になる
ここで、Halogen Hookを用いることでSimpleなものを組み合わせて作ることがComponentレベルでできるようになる
すごく嬉しいmrsekut.icon*3