5
3

More than 5 years have passed since last update.

PureScriptでAjaxするときはRemoteDataも使おう

Last updated at Posted at 2019-04-29

前書き

purescript-affjax はajaxする際のデファクトスタンダードと言って良いと思います。
通信が成功したか失敗したかはEitherで受け取ることができますが、実際にはEitherMaybeを直に取り扱うのは辛いです。

動機付けとして、Halogenのexamplesにaffjaxを使う例( effects-aff-ajax )があるので見てみます。以下のようにMaybeでモデル化されています。

-- PureScript
type State =
  { loading :: Boolean
  , result :: Maybe String
  }

HalogenとAffjaxを連携させる例としては十分ですが、実際には扱いづらいです。

RemoteData

Elmのチュートリアル( HTTP )では以下のようにモデルを設計しています。

-- Elm
type Model
  = Failure
  | Loading
  | Success String

Maybeと上記モデルを改めて比べてみると、前者はFailureLoadingという区別されるべき2つの状態を全てNothingとして忘却してしまうので扱いづらくなってしまっていることがよく分かります。

Maybeを利用した設計の問題点は How Elm Slays a UI Antipattern でも説明されています。
そして、同記事で紹介されているのがRemoteDataと名付けられたカスタム型です。

-- Elm
type RemoteData e a
    = NotAsked
    | Loading
    | Failure e
    | Success a

使ってみる

PureScriptへの移植版であるpurescript-remotedata パッケージがあるのでそれを使います。
もし独自で実装する際は、Elmとは異なりdataを使います。

--purescript
data RemoteData e a
  = NotAsked
  | Loading
  | Failure e
  | Success a

以下は良く使いそうな関数の例です。

fromEither :: forall e a. Either e a     -> RemoteData e a
isLoading  :: forall e a. RemoteData e a -> Boolean

今回は以下のようにインポートして使います。

import Network.RemoteData (RemoteData(..))
import Network.RemoteData (fromEither, isLoading) as RemoteData

RemoteDataを使うと冒頭のモデルは以下のように書けます。

-- PureScript
type State = { request :: RemoteData AX.ResponseFormatError String }

実際にアクションと結びつけると以下のようになるかと思います。

handleAction ActionName = do
  response <- H.liftAff $ AX.get AX.ResponseFormat.string ("URL")
  H.modify_ $ _ { request = RemoteData.fromEither response.body }

Submitボタンを作る

submitButton
  :: forall e a m
   . RemoteData e a
  -> String
  -> H.ComponentHTML Action () m
submitButton remote text = HH.button
  [ HP.disabled $ RemoteData.isLoading remote
  , HP.type_ HP.ButtonSubmit
  ]
  [ HH.text text ]

ロード中はdisabledをtrueにするという処理が簡単に書けます。

UIのパターンマッチング

view :: forall e m. RemoteData e String -> H.ComponentHTML Action () m
view remote = HH.div_
  case remote of
    NotAsked ->
      [ HH.p_ [HH.text "Enter username and click button"]]
    Loading ->
      [ HH.p_ [HH.text "Working..."] ]
    Failure _ ->
      [ HH.p_ [HH.text "Failed for some reasons. Please try again!"]]
    Success res ->
      [ HH.h2_
          [ HH.text "Response:" ]
      , HH.pre_
          [ HH.code_ [ HH.text res ] ]
      ]

パターンマッチングでコードが読みやすいです。

結論

AffjaxとRemoteDataはセットで使おう!

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3