ゼロから始めるstimulus入門4(状態の管理)

## はじめに
今回はstimulusの状態管理についてドキュメントを見ながら学んでいきます。

過去回↓
第1回：[ゼロから始めるstimulus入門1(ファーストコンタクト)](https://qiita.com/shinry/items/de697c4a679e14a41ffa)
第2回：[ゼロから始めるstimulus入門2(現実的なものを作る)](https://qiita.com/shinry/items/34ca225c098aeb91b160)
第3回：[ゼロから始めるstimulus入門3(古いブラウザを考慮した設計)](https://qiita.com/shinry/items/b2b732b6aeb4374d146d)

## 状態の管理
最近のフレームワークのほとんどは、常に JavaScript で状態を維持することを推奨されていますが、stimulusではDOM内の属性として状態が存在します。
この方法によって、最初のHTMLドキュメント、Ajaxリクエスト、Turboのページ遷移、さらには他のJavaScriptライブラリなど、どこからでも HTML を操作でき、明示的な初期化手順なしで、関連付けられたコントローラーが自動的に起動するようです。

## スライドショーの作成
スライドショーの実装を例に、学んでいきます。
### slideshow_controller.js
- targetsでslideを定義する
- initialize()で初期状態の表示をthis.element.dataset.indexから読み取る(後述のdata-indexで指定した値と対応する)
- showCurrentSlide()メソッドを呼び出し、indexが一致する場合にhidden属性を切り替えるメソッドを定義する
- next()、previous()のメソッドで現在のスライドを進めたり巻き戻したりする
```js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "slide" ]

  initialize() {
    this.index = Number(this.element.dataset.index)
    this.showCurrentSlide()
  }

  next() {
    this.index++
    this.showCurrentSlide()
  }

  previous() {
    this.index--
    this.showCurrentSlide()
  }

  showCurrentSlide() {
    this.slideTargets.forEach((element, index) => {
      element.hidden = index !== this.index
    })
  }
}
```

### html
- data-indexで初期状態のtargetを指定する
```html
<div data-controller="slideshow" data-index="0">
  <button data-action="slideshow#previous"> ← </button>
  <button data-action="slideshow#next"> → </button>

  <div data-slideshow-target="slide">🐵</div>
  <div data-slideshow-target="slide">🙈</div>
  <div data-slideshow-target="slide">🙉</div>
  <div data-slideshow-target="slide">🙊</div>
</div>
```

### 実際の画面
←矢印または→矢印を押すことで、スライドの切り替えを行うことができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1328154/b0582c0d-1215-4a0b-6fa4-15870dcef395.png)

## スライドショーの作成(リファクタリング)
さきほどの実装だと、indexに再度アクセスしたり、indexを増分して結果を DOM に保持したりする場合の対応が難しいです。なので以下のようにリファクタリングします。
### slideshow_controller.js
- `static values = { index: Number }`を定義する
    - これはhtmlのdata-slideshow-index-value属性に対応するthis.indexValueというコントローラーのプロパティを作成し、数値への変換も自動的に処理する
- showCurrentSlide()もnext(),previous()で呼び出されていたため、indexValueChanged()を定義する
    - indexValueChanged()初期化時および属性の変更に応じてメソッドを呼び出す
```js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "slide" ]
  static values = { index: Number }

  next() {
    this.indexValue++
  }

  previous() {
    this.indexValue--
  }

  indexValueChanged() {
    this.showCurrentSlide()
  }

  showCurrentSlide() {
    this.slideTargets.forEach((element, index) => {
      element.hidden = index !== this.indexValue
    })
  }
}
```

### html
- data-slideshow-index-valueを定義
    - static valuesで定義した部分と対応する
```html
<div data-controller="slideshow" data-slideshow-index-value="1">
  <button data-action="slideshow#previous"> ← </button>
  <button data-action="slideshow#next"> → </button>

  <div data-slideshow-target="slide">🐵</div>
  <div data-slideshow-target="slide">🙈</div>
  <div data-slideshow-target="slide">🙉</div>
  <div data-slideshow-target="slide">🙊</div>
</div>
```

### デフォルト値をあらかじめ指定
以下のようにstatic valuesで定義する際にデフォルトの値を設定することも可能
```js
static values = { index: { type: Number, default: 2 } }
```

## 補足
### ライフサイクルコールバック
以下はstimulusのライフサイクルコールバックメソッド。コントローラーがdocumentに入る時や離れる時に関連する状態を設定または開放するのに役立つ。
| メソッド       | Stimulusによって呼び出されるタイミング                      |
| -------------- | ------------------------------------------------------------- |
| `initialize()` | コントローラーが最初にインスタンス化されるときに一度実行        |
| `connect()`    | コントローラーがDOMに接続されるたびに実行                        |
| `disconnect()` | コントローラーがDOMから切断されるたびに実行                      |

### static values
static valuesという定義はtargetsと同様に、stimulusのコントローラー内で値を定義する際に使用する。
`static values = { index: Number }`この場合は、indexを定義し、それをNumber(数値型)で受け取ることを示す。
つまり、
htmlで`data-slideshow-index-value="1"`と設定している場合、1を数値型として変換し受け取る。

## 次回
stimulusの状態の管理方法について学ぶことができました。
さらにドキュメントを見ながら学んでいきます。