zfb
GitHub リポジトリ

検索したい単語を入力

いつでも検索バーを開ける

条件分岐とリスト

Show で所有スコープを持つ条件分岐を作り、For でキー付きリストをトランザクションとして更新します。

Show による条件分岐

Show は読み取り専用の真偽値 signal から分岐を選びます。children と省略可能な fallback はファクトリで、選ばれた分岐は新しい子スコープの下で作られます。

import { Show, signal } from "@takazudo/zfb/zudo-react";

function Details() {
  const expanded = signal(false);

  return (
    <section>
      <button on:click={() => { expanded.value = !expanded.value; }}>
        Toggle details
      </button>
      <Show
        when={expanded}
        fallback={() => <p>More details are available.</p>}
      >
        {() => <p>The expanded content is mounted here.</p>}
      </Show>
    </section>
  );
}

選ばれたファクトリは、その分岐が生きている間に一度実行されます。別の分岐に切り替えるとスコープを一度破棄して領域を削除し、戻すと新しい分岐インスタンスを作ります。false で fallback がなければ子スコープは作りません。リアクティブな hidden 属性は既存の DOM を隠すだけで、分岐やそのリソースは破棄しません。

For によるキー付き項目

For は読み取り専用の配列 signal を読み、各通常の項目に by を呼んで文字列または有限数のキーを得ます。

import { For, signal } from "@takazudo/zfb/zudo-react";

function TodoList() {
  const todos = signal([
    { id: "a", title: "Read the guide" },
    { id: "b", title: "Ship the feature" }
  ]);

  return (
    <ul>
      <For each={todos} by={(todo) => todo.id}>
        {(todo, index) => (
          <li>{index}: {todo.value.title}</li>
        )}
      </For>
    </ul>
  );
}

item と index の引数は読み取り専用の signal です。JSX に渡すと更新されるバインディングになり、value を読むとスナップショットになります。維持されたキーでは、領域、子スコープ、ref、イベントハンドラー、コントロールの状態、item/index の signal オブジェクトが保たれます。同じキーの別オブジェクトに置き換えると読み取り専用のビューは更新されますが、ファクトリは再実行されません。

キーは文字列か有限数です。負のゼロはゼロに正規化されますが、数値の 1 と文字列の "1" は別のキーです。For は DOM やスコープの状態を変える前に次のキー全体を検証します。重複キーは ZR_DUPLICATE_KEY、未対応のキーは ZR_KEY_TYPE を報告し、元のリストはそのままです。初期キーの重複はサーバーレンダリングを失敗させ、ハイドレーション中の不正なキーはその島を失敗させます。

古い領域を置き換える前に、新しい項目を仮に構築します。セットアップに失敗したら仮のスコープを破棄し、元のリストを有効なまま残します。キーを削除するとそのスコープを一度だけ破棄し、後で同じキーを追加すると新しい項目を作ります。維持されたキーを移動する場合は既存の DOM を移動し、可能な限りフォーカスと選択範囲を保ちます。

動的なテーブル行

For は tbody の内部には置けません。コメントで区切られる領域は、そのテーブル構造上の位置では有効ではありません。行をまとめて入れ替えるには、1 要素だけを持つ For をテーブルの外側に置き、item のスナップショットから直接 tr 要素を作ります。

import { computed, For, signal } from "@takazudo/zfb/zudo-react";

function ResultsTable() {
  const data = signal({
    rev: 0,
    rows: [{ id: "a", name: "First" }]
  });
  const table = computed(() => {
    const { rev, rows } = data.value;
    return [{ rev, rows }];
  });
  // To replace the rows:
  // data.value = { rev: data.value.rev + 1, rows: nextRows };

  return (
    <section>
      <For each={table} by={(item) => item.rev}>
        {(item) => {
          const { rows } = item.value;
          return (
            <table>
              <tbody>
                {rows.map((row) => <tr><td>{row.name}</td><td><input aria-label={row.name} /></td></tr>)}
              </tbody>
            </table>
          );
        }}
      </For>
    </section>
  );
}

revision が変わるたびに古いテーブルとそのスコープを破棄し、新しいテーブルを作ります。古いテーブル内のフォーカス、選択範囲、スクロール位置、フォームコントロールの状態は失われます。セルの値だけが変わる場合は、テーブルを入れ替えずにセルへ signal のライブバインディングを使えます。

パーサー上の位置と変換中の入力

For と Show の領域は、コメントで区切った範囲が有効な位置に置く必要があります。table の構造上の階層や select の選択肢の内部では使えません。特殊な HTML の中に動的領域を置く前に、パーサーの文脈に関する規則を確認してください。

フォーカス中の項目がテキストを変換しているとき、その項目を切り離す並べ替えは変換終了または blur まで待ってから最新のリストを適用します。その項目を削除する操作は明示的な unmount であり、リソースを取り消します。

関連ページ

Revision History

作成更新