Guide / Reinhardt 0.3.15

変数を一つ変えたとき、画面のどこが動くのか

この記事の内容

二つの表示がleftという数値を使っています。一方はleftrightを切り替えられ、もう一方は常にleftを表示します。前者をrightへ切り替えると、その後のleftへの書き込みは後者だけを更新します。選択を戻すと、前者も現在のleftの値を読みます。

実行可能なブラウザー用サンプルはReinhardt Pages 0.3.15と同梱のロックファイルを使います。七つの状態で、表示テキスト、Effectの実行回数、依存グラフを検証します。Reinhardt Pressはプロジェクト自身の広報媒体です。

実行した読み取りが矢印を作る

選択表示を動かす部分の抜粋です。Signalのハンドル、出力先のテキストノード、実行回数のカウンターを複製してクロージャーへ渡しています。マウントと寿命の管理は完全版に含まれます。

Effect::new(move || {
    let value = if show_left.get() {
        left.get()
    } else {
        right.get()
    };
    runs.set(runs.get() + 1);
    output.set_data(&value.to_string());
})

アクティブなObserverの下でget()を呼ぶと、値を取得すると同時に依存が記録されます。show_leftがtrueなら、この実行が読むのはshow_leftleftです。ソースにright.get()が書かれていても、その呼び出しは実行されません。選択表示のEffectにはスイッチとleftから矢印が入ります。もう一つのEffectは独立してleftを読みます。

固定した版のSignal::get実装は、ランタイムへ読み取りを記録します。Effect::execute_effectは再実行前に古い依存を消し、次の実行に使うObserverを設定します。グラフが追うのは直前の実行で読んだ値です。

実ブラウザーで確認した七状態

次の表は同梱のブラウザー回帰テストで観測した結果です。実行回数には生成時の一回を含めます。各setterは保留中の更新を明示的に実行し、書き込みを一つずつ検証できるようにしています。

操作選択表示left専用表示選択表示の実行回数left専用の実行回数選択表示の依存先
初期状態101011show_left, left
right = 101101011show_left, left
left = 11111122show_left, left
show_left = false1011132show_left, right
left = 121011233show_left, right
right = 1021021243show_left, right
show_left = true121253show_left, left

最初のrightへの書き込みでは、どちらのEffectも動きません。次のleftへの書き込みでは両方が動きます。分岐の切り替えは選択表示だけを再実行し、その依存先をleftからrightへ置き換えます。ここでleft = 12としても、選択表示のEffectは三回のままです。left専用表示だけが変わります。

選択を戻すと12が出ます。通知に反応しなかったからといって、保存された値が止まるわけではありません。新しい実行が現在の値を読み、leftへの矢印を再び作ります。テストでは、選択表示に元のテキストノードが残っていることと、デモの破棄で両Effectがランタイムから消えることも確認します。二つの実装同士の一致ではなく、独立に記述した期待状態と比較しています。

どのAPIが依存を追っているか

この例は低水準のEffect::new、明示的にマウントしたブラウザーのテキストノード、Text::set_dataを使います。page!が生成するバインディング、ハイドレーション、コンポーネントの再マウントは検証していません。このノード同一性テストから、より広い保証までは導けません。

似た名前のuse_effect(callback, deps)には、明示的な依存タプルを渡します。そのコールバックはここで使う自動追跡のObserverなしで動き、購読は指定された依存に従います。固定した版のPagesフックの説明を参照してください。get_untracked()も依存を追加せずに値を読みます。予想外の更新を調べる際は、使っているAPIと実際の読み取りを確認します。

サンプルは固定した版の非公開扱いの診断メソッドdebug_dependenciesで依存名を表示します。実験の観察用であり、安定したアプリ連携APIとしては扱っていません。

一つの読み取りを変えて試す

ZIPを展開してsignal-dependenciesディレクトリーへ入ります。Rust 1.96以降、wasm-pack、ChromeまたはChromium、対応するChromeDriverをPATHに用意します。標準以外のブラウザーを選ぶ方法はREADMEにあります。

rustup target add wasm32-unknown-unknown
wasm-pack test --headless --chrome -- --nocapture

アサーションが通ると、SIGNAL_EVIDENCE=に続いてJSONが出ます。同じライブラリーを小さな対話アプリとして使うには、次を実行します。

wasm-pack build --target web --dev -- --locked
python3 -m http.server 8091 --bind 127.0.0.1

http://127.0.0.1:8091/を開きます。初回ビルドでは依存を取得してコンパイルします。データベース、Docker、npm、Reinhardtのサーバーは不要です。動画は編集した説明で、導入時間の計測映像ではありません。

三つ目の値や入れ子の条件を加えてみてください。分岐ごとに依存先を予想してから、値と実行回数の両方を検証します。非アクティブな値で選択表示が再実行された場合は、最小の再現例、ロックファイル、ブラウザーの版、正確なコマンドと出力を残します。貢献ガイドリアクティブ処理のテストソースが、依存追跡の回帰テストを加える入口になります。

ここで数えたのはEffectのクロージャー呼び出しです。描画時間、ネットワーク遅延、フレームレート、アプリ全体の高速化、本番のスケジューリング動作は測定していません。