---
title: 'リスト'
---

import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'

## イテレータ

イテレータから HTML を構築する方法は 4 つあります：

<Tabs>
  <TabItem value="`for` loops" label="`for` ループ">
主なアプローチは for ループを使用することです。これは Rust に既に存在する for ループと同じですが、1 つの重要な違いがあります：通常の for ループは何も返せませんが、`html!` 内の for ループはノードのリストに変換されます。

```rust
use yew::prelude::*;

html! {
    for i in 0 .. 10 {
        <span>{i}</span>
    }
};
```

`for` ループ本体では、html 子要素の前に Rust の文を置くことができます。終端子を持つ文であれば何でも動作します：`let` バインディング、`;` で終わる式文、アイテム定義（`fn`、`struct`、`use` など）、`;` 付きのマクロ呼び出しです。

```rust , ignore
use yew::prelude::*;

html! {
    for item in items {
        let label = format!("{}: {}", item.id, item.name);
        let class = if item.active { "active" } else { "inactive" };
        <div class={class}>{label}</div>
    }
};
```

`break` と `continue` は通常の Rust のループと同様に動作し、出力されるノードリストに影響します。
`continue` は現在の反復でノードを生成せずにスキップし、`break` は反復を早期に終了します：

```rust
use yew::prelude::*;

html! {
    for i in 0..10 {
        if i % 2 == 0 {
            continue
        }
        if i > 7 {
            break
        }
        <span>{i}</span>
    }
};
```

`break` と `continue` は、波括弧付きでも波括弧なしでも、match アームの本体としてそのまま使えます：

```rust
use yew::prelude::*;

html! {
    for i in 0..10 {
        match i {
            0 => continue,
            8.. => break,
            _ => <span>{i}</span>,
        }
    }
};
```

ループラベルもサポートされています。`break 'label` や `continue 'label` は、マクロを囲む Rust コードで定義されたラベル付きループを対象にでき、マクロの内側から外側のループを抜けることができます：

```rust , ignore
use yew::prelude::*;

let mut rendered = Vec::new();
'outer: for section in sections {
    rendered.push(html! {
        for item in section.items {
            if should_stop(&item) {
                break 'outer;
            }
            <span>{item.name}</span>
        }
    });
}
```

:::note 限定パス

`<Type>::method(...)` のような裸の限定パス式は `<Type>` という要素開始タグと衝突し、受け付けられません。回避方法は 2 つあります：

- 末尾に `;` を付けてプリアンブルの式文にします：`<Type>::method(...);`。戻り値は破棄されます。
- `{...}` で囲んで戻り値をノードとして使います：`{ <Type>::method(...) }`。

:::

:::caution `if` / `match` / `while` / 外側の `for` の内部で本体が裸の `{expr}` だけの `for`

`for` ループの本体に裸の `{expr}` ブロックしか書かれていない場合、`html!` の**トップレベル**にあれば反復ごとに 1 ノードずつ描画されます。ところが同じコードを `if` / `match` のアーム / `while` / 外側の `for` の本体に入れ子で置くと、`mismatched types: expected (), found T` というエラーで拒否されます。トップレベルと入れ子の位置とでは解析方針が異なり、入れ子の位置ではループをまず Rust の文として解析しようとします。本体が完全に Rust として解析可能であれば（裸の `{expr}` ブロックはまさにそうです）、html の制御フローとして扱われなくなります。

```rust , ignore
// コンパイル可能：for はトップレベルにあります。
html! {
    for _ in 0..9 {
        {my_foo}
    }
}

// コンパイル不可：for は `if` の内部に入れ子になっています。
html! {
    if condition {
        for _ in 0..9 {
            {my_foo}
        }
    }
}
```

`my_foo` を条件付きで 9 回描画したい場合は、次のいずれかを選んでください：

```rust , ignore
// 1. 子要素を html 要素で囲む（推奨）。
html! {
    if condition {
        for _ in 0..9 {
            <span>{my_foo}</span>
        }
    }
}

// 2. for をネストした `html!` のトップレベルへ移す。
html! {
    if condition {
        { html! {
            for _ in 0..9 {
                {my_foo}
            }
        } }
    }
}

// 3. イテレータと `collect::<Html>()` でリストを組み立てる。
html! {
    if condition {
        { (0..9).map(|_| html!({my_foo})).collect::<Html>() }
    }
}
```

同じ規則は `match` のアーム本体、`while` の本体、外側の `for` の本体にも当てはまります。

:::

  </TabItem>
  <TabItem value="`while` loops" label="`while` ループ">
`while` および `while let` ループは `for` と同じように動作し、本体からノードのリストを生成します。`for` 本体で受け付けられるすべての文の形式（let バインディング、式文、アイテム、マクロ）がここでも受け付けられ、`if` ブロックと `break` / `continue` も使えます。

```rust
use yew::prelude::*;

let mut items = vec!["a", "b", "c", "skip", "d"].into_iter();

html! {
    while let Some(item) = items.next() {
        if item == "skip" {
            continue
        }
        if item.is_empty() {
            break
        }
        <span>{item}</span>
    }
};
```

条件付きの通常の `while` も使用できます：

```rust
use yew::prelude::*;

let mut counter: i32 = 0;

html! {
    while counter < 5 {
        let current = counter;
        counter += 1;
        <span>{current}</span>
    }
};
```

  </TabItem>
  <TabItem value="`for` block" label="`for` ブロック">
もう一つの方法は `for` キーワードを使用することです。これはネイティブの Rust 構文ではなく、HTML マクロによってイテレータを表示するために必要なコードを出力します。
この方法は、イテレータが既に計算されていて、マクロに渡すだけでよい場合に最初の方法より適しています。

```rust
use yew::prelude::*;

let items = (1..=10).collect::<Vec<_>>();

html! {
    <ul class="item-list">
        { for items.iter() }
    </ul>
};
```

  </TabItem>
  <TabItem value="`collect` method" label="`collect` メソッド">

最後の方法は、イテレータの最終変換で `collect::<Html>()` を呼び出すことで、Yew が表示できるリストを返します。

```rust
use yew::prelude::*;

let items = (1..=10).collect::<Vec<_>>();

html! {
    <ul class="item-list">
        { items.iter().collect::<Html>() }
    </ul>
};
```

  </TabItem>
</Tabs>

## キー付きリスト

キー付きリストは、すべての子要素にキーがある最適化されたリストです。
`key` は Yew が提供する特別な属性で、HTML 要素やコンポーネントに一意の識別子を与え、Yew 内部での最適化に使用されます。

:::caution
キーは各リスト内で一意である必要があり、HTML の `id` のようにグローバルに一意である必要はありません。リストの順序に依存してはいけません。
:::

リストにキーを追加することを常にお勧めします。

一意の `String`、`str`、または整数を特別な `key` 属性に渡すことでキーを追加できます。

```rust , ignore
use yew::prelude::*;

let names = vec!["Sam","Bob","Ray"]

html! {
    <div id="introductions">
        {
            names.into_iter().map(|name| {
                html!{<div key={name}>{ format!("Hello, I'am {}!",name) }</div>}
            }).collect::<Html>()
        }
    </div>
};

```

### パフォーマンスの最適化

キー付きリストのパフォーマンス向上をテストするための[例](https://github.com/yewstack/yew/tree/master/examples/keyed_list)があります。以下は簡単なテスト手順です：

1. [オンラインデモ](https://examples.yew.rs/keyed_list)にアクセスします。
2. 500個の要素を追加します。
3. キーを無効にします。
4. リストを反転します。
5. "最後のレンダリングにかかった時間 Xms" を確認します（この記事の執筆時点では約60ms）。
6. キーを有効にします。
7. 再度リストを反転します。
8. "最後のレンダリングにかかった時間 Xms" を確認します（この記事の執筆時点では約30ms）。

この記事の執筆時点では、500個のコンポーネントに対して速度が2倍に向上しました。

### 原理の説明

通常、リストを反復処理する際には、各リスト項目にキーを追加するだけで済みます。データの順序が変わる可能性があるためです。
リストを再レンダリングする際に、キーは調整プロセスを高速化するために使用されます。

キーがない場合、例えば `["bob", "sam", "rob"]` を反復処理すると、最終的に以下のようなHTMLが生成されます：

```html
<div id="bob">My name is Bob</div>
<div id="sam">My name is Sam</div>
<div id="rob">My name is rob</div>
```

次のレンダリングでリストが `["bob", "rob"]` に変更された場合、Yew は id="rob" の要素を削除し、id="sam" を id="rob" に更新できます。

各要素にキーを追加すると、初期の HTML は変わりませんが、変更後のリスト `["bob", "rob"]` をレンダリングすると、Yew は2番目の HTML 要素のみを削除し、他の要素はそのまま残ります。キーを使用して要素を関連付けることができるためです。

コンポーネントから別のコンポーネントに切り替える際に、両方に最高レンダリング要素として div がある場合にバグ/"機能" に遭遇した場合。
Yew はこれらの状況で最適化として既にレンダリングされた HTML div を再利用します。
その div を再利用せずに再作成する必要がある場合は、異なるキーを追加することで再利用されなくなります。

## さらなる読み物

- [TodoMVC の例](https://github.com/yewstack/yew/tree/master/examples/todomvc)
- [キー付きリストの例](https://github.com/yewstack/yew/tree/master/examples/keyed_list)
- [ルーティングの例](https://github.com/yewstack/yew/tree/master/examples/router)
