😸

[elm-spa v6] Shared Stateですべてのページに必要な状態を管理する

2022/04/23に公開

elm-spaは、ElmでSPAをするためのボイラプレートを生成してくれる、とても便利なツールです。バージョン6で仕様が変わった部分があるので、変更箇所をお伝えする記事です。今回はすべてのページで必要な状態を管理するShared State について解説したいと思います。今回の知識を発展させるとelm-spa v6でユーザ認証実装
で書いたように認証機構も作れるので、よければ合わせて御覧ください。

今回のサンプルは、時刻・カレンダー表示とパソコンのタイムゾーンに合わせる設定画面の3画面の遷移ができるSPAです。初期状態ではタイムゾーンはUTCとなっており、以下のデモでは最初の時点で、PCの時刻とズレが生じています。設定画面でタイムゾーンを設定した場合、画面遷移をしてもタイムゾーンがJSTとして計算がされるようになります。

Image from Gyazo

デモのコードはこちら

https://github.com/ababup1192/elm-spa-shared

デモコードを解説

デモコードを解説していこうと思います。ソースコードはPagesディレクトリと直下のShared.elmに分かれます。Pagesはそのまま時刻・日付・設定画面の3つに対応しています。Shared.elmがShared Stateを管理するファイルになります。

src
├── Pages
│   ├── Calendar.elm
│   ├── Home_.elm
│   └── Settings.elm
└── Shared.elm

時刻表示画面(Home_.elm)

Modelには、時間を保存するための変数timeMaybeを定義し、initでは、時間取得のためのCmdを発行します。

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Pages/Home_.elm#L29-L38

updateでは、時間を保存します。

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Pages/Home_.elm#L45-L53

時間を表示をしたり、elm-spaが自動生成したGen.Routeモジュールを使ってリンクを生成したりしています。時間を表示するときにModelではなく、Shared.Modelからタイムゾーンを参照しているのがポイントとなります。このShared.Modelはどこから来たのでしょうか?

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Pages/Home_.elm#L60-L88

答えは、page関数が一般的なElmファイルで言うmain関数の役割に対応していて、Shared.Modelinit, update, view, subscriptions関数に任意に渡すことができます。例えば、initに渡してModelとして設定することもできます。状況によって使い分けてください。

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Pages/Home_.elm#L15-L22

日付表示画面(Calendar.elm)

基本的には、時刻表示画面と代わりがありません。

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Pages/Calendar.elm#L16-L23

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Pages/Calendar.elm#L132-L145

タイムゾーン設定画面(Settings.elm)

時刻と日付の表示画面は、Shared Stateを使う画面でした。設定画面はその逆でShared Stateを変更する画面です。変更が必要なページはPage.elementではなく、Page.advancedである必要があります。

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Pages/Settings.elm#L17-L24

advancedの場合、Cmd msgではなく、Shared SatateのCmdとPageのCmd msgの2つをマージして抽象化した、Effectという型になります。

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Pages/Settings.elm#L31-L37

PageのCmdをEffectにするには、Effect.fromCmd関数。SharedのMsgをEffectにするには、Effect.fromShared関数でそれぞれ変換する必要があります。

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Pages/Settings.elm#L44-L56

viewは基本的なページと変わり有りません。

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Pages/Settings.elm#L63-L76

Shared.elm

Sharedは基本的にviewがない(用意しても構いません)Elmファイルのようなものです。タイムゾーンを持ち、updateで更新します。

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Shared.elm#L19-L36

Msgは外から使えるように、中身もexposeします。

https://github.com/ababup1192/elm-spa-shared/blob/master/src/Shared.elm#L1-L8

注意点

今回は、Shared Stateを使う画面と変更する画面で分かれていましたが、もし使うと変更が同じ画面の場合には注意が必要です。Shared Stateがその画面中で変わったとしてもそのPageのviewには即時反映されません。なぜなら、Msg, update, Modelが別物だからです。そのため、もし変更を追従したい場合には、initでShared.ModelModelへセットして、Shared Stateの変更と共にModelの変更も行うと良いでしょう。

init shared =
    { timezone = shared.timezone
    }
   

update msg model =
    case msg of
        ChangeShared current ->
	    ({ model | timezone = current }, Effect.fromShared <| ChangeTimezone current )
	    
	    
view model = -- Shared.Modelは使わない
    p [] [ text <| calc model.timezone ]

まとめ

elm-spaはとても便利なツールですが、一つ一つの特性を理解して使わなければ真価を発揮できません。うまく利用して、SPAを便利に作りましょう。

Discussion