Stimulus 6: 外部リソースの操作

:::note
この記事は[Google翻訳](https://translate.google.co.jp/)の結果を編集したものです。
:::

https://stimulus.hotwired.dev/handbook/working-with-external-resources

前の章では値を使用してコントローラーの内部状態をロードして永続化する方法を学びました。

コントローラーは外部リソースの状態を追跡する必要がある場合があります。外部とはDOMまたはStimulusの一部にないものを意味します。たとえばHTTPリクエストを発行し、リクエストの状態が変化したときに応答する必要がある場合があります。またはタイマーを開始してコントローラーが接続されなくなったときに停止することもできます。この章ではこれらの両方を行う方法を見ていきます。

# HTMLの非同期読み込み

HTMLのリモートフラグメントを読み込んで挿入することにより、ページの一部を非同期的に設定する方法を学びましょう。Basecampでこの手法を使用して最初のページの読み込みを高速に保ち、ビューをユーザー固有のコンテンツから解放してより効果的にキャッシュできるようにします。

サーバーから取得したHTMLを要素に取り込む汎用コンテンツローダーコントローラーを作成します。 次に、これを使用してメールの受信トレイに表示されるような未読メッセージのリストを読み込みます。

`public/index.html`で受信トレイをスケッチすることから始めます。

```html
<div data-controller="content-loader"
     data-content-loader-url-value="/messages.html"></div>
```

次にメッセージリスト用のHTMLを含む新しい`public/messages.html`ファイルを作成します。

```html
<ol>
  <li>New Message: Stimulus Launch Party</li>
  <li>Overdue: Finish Stimulus 1.0</li>
</ol>
```

（実際のアプリケーションではこのHTMLをサーバー上で動的に生成しますが、デモ用に静的ファイルを使用します。）

これでコントローラーを実装できます。

```javascript
// src/controllers/content_loader_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static values = { url: String }

  connect() {
    this.load()
  }

  load() {
    fetch(this.urlValue)
      .then(response => response.text())
      .then(html => this.element.innerHTML = html)
  }
}
```

コントローラーが接続すると要素の`data-content-loader-url-value`属性で指定されたURLへの[Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)リクエストを開始します。次に返されたHTMLを要素の`innerHTML`プロパティに割り当てて読み込みます。

ブラウザーの開発者コンソールでネットワーク タブを開き、ページをリロードします。最初のページの読み込みを表すリクエストが表示され、その後にコントローラーの`messages.html`へのリクエストが続きます。

# タイマーで自動リフレッシュ

受信トレイを定期的に更新して常に最新の状態に保つように変更して、コントローラーを改善しましょう。

`data-content-loader-refresh-interval-value`属性を使用してコントローラーがコンテンツをリロードする頻度をミリ秒単位で指定します。

```html
<div data-controller="content-loader"
     data-content-loader-url-value="/messages.html"
     data-content-loader-refresh-interval-value="5000"></div>
```

これでコントローラーを更新して間隔を確認し、存在する場合は更新タイマーを開始できます。

コントローラーに静的値の定義を追加し、新しいメソッド`startRefreshing()`を定義します。

```javascript
export default class extends Controller {
  static values = { url: String, refreshInterval: Number }

  startRefreshing() {
    setInterval(() => {
      this.load()
    }, this.refreshIntervalValue)
  }

  // …
}
```

次にインターバル値が存在する場合に`startRefreshing()`を呼び出すように`connect()`メソッドを更新します。

```javascript
connect() {
  this.load()

  if (this.hasRefreshIntervalValue) {
    this.startRefreshing()
  }
}
```

ページをリロードし、開発者コンソールで5秒ごとに新しいリクエストを観察します。次に`public/messages.html `変更を加えて受信トレイに表示されるまで待ちます。

# 追跡されたリソースを解放する

コントローラーが接続されたときにタイマーを開始しますが、停止することはありません。つまり、コントローラーの要素が消えてもコントローラーはバックグラウンドでHTTPリクエストを発行し続けることになります。

この問題はタイマーへの参照を保持するように`startRefreshing()`メソッドを変更することで修正できます。

```javascript
startRefreshing() {
  this.refreshTimer = setInterval(() => {
    this.load()
  }, this.refreshIntervalValue)
}
```

次に、対応する`stopRefreshing()`メソッドを以下に追加してタイマーをキャンセルできます。

```javascript
stopRefreshing() {
  if (this.refreshTimer) {
    clearInterval(this.refreshTimer)
  }
}
```

最後に、コントローラーが切断されたときにタイマーをキャンセルするようにStimulusに指示するために`disconnect()`メソッドを追加します。

```javascript
disconnect() {
  this.stopRefreshing()
}
```

これでコンテンツローダーコントローラーがDOMに接続されている場合にのみリクエストを発行することを確認できます。

最終的なコントローラークラスを見てみましょう。

```javascript
// src/controllers/content_loader_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static values = { url: String, refreshInterval: Number }

  connect() {
    this.load()

    if (this.hasRefreshIntervalValue) {
      this.startRefreshing()
    }
  }

  disconnect() {
    this.stopRefreshing()
  }

  load() {
    fetch(this.urlValue)
      .then(response => response.text())
      .then(html => this.element.innerHTML = html)
  }

  startRefreshing() {
    this.refreshTimer = setInterval(() => {
      this.load()
    }, this.refreshIntervalValue)
  }

  stopRefreshing() {
    if (this.refreshTimer) {
      clearInterval(this.refreshTimer)
    }
  }
}
```

# アクションパラメーターの使用

ローダーを複数の異なるソースで動作させたい場合、アクションパラメーターを使用して実行できます。 次のHTMLを使用します。

```html
<div data-controller="content-loader">
  <a href="#" data-content-loader-url-param="/messages.html" data-action="content-loader#load">Messages</a>
  <a href="#" data-content-loader-url-param="/comments.html" data-action="content-loader#load">Comments</a>
</div>
```

次に`load`アクションを介してこれらのパラメーターを使用できます。

```javascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  load({ params }) {
    fetch(params.url)
      .then(response => response.text())
      .then(html => this.element.innerHTML = html)
  }
}
```

パラメーターを破棄して、URLパラメータを取得することもできます。

```javascript
load({ params: { url } }) {
  fetch(url)
    .then(response => response.text())
    .then(html => this.element.innerHTML = html)
}
```

# まとめと次のステップ

この章ではStimulusライフサイクルコールバックを使用して外部リソースを取得および解放する方法を見てきました。

次に独自のアプリケーションにStimulusをインストールして構成する方法を見ていきます。