Guide / Reinhardt 0.3.15
変数を一つ変えたとき、画面のどこが動くのか
この記事の内容
二つの表示がleftという数値を使っています。一方はleftとrightを切り替えられ、もう一方は常に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_leftとleftです。ソースにright.get()が書かれていても、その呼び出しは実行されません。選択表示のEffectにはスイッチとleftから矢印が入ります。もう一つのEffectは独立してleftを読みます。
固定した版のSignal::get実装は、ランタイムへ読み取りを記録します。Effect::execute_effectは再実行前に古い依存を消し、次の実行に使うObserverを設定します。グラフが追うのは直前の実行で読んだ値です。
実ブラウザーで確認した七状態
次の表は同梱のブラウザー回帰テストで観測した結果です。実行回数には生成時の一回を含めます。各setterは保留中の更新を明示的に実行し、書き込みを一つずつ検証できるようにしています。
| 操作 | 選択表示 | left専用表示 | 選択表示の実行回数 | left専用の実行回数 | 選択表示の依存先 |
|---|---|---|---|---|---|
| 初期状態 | 10 | 10 | 1 | 1 | show_left, left |
| right = 101 | 10 | 10 | 1 | 1 | show_left, left |
| left = 11 | 11 | 11 | 2 | 2 | show_left, left |
| show_left = false | 101 | 11 | 3 | 2 | show_left, right |
| left = 12 | 101 | 12 | 3 | 3 | show_left, right |
| right = 102 | 102 | 12 | 4 | 3 | show_left, right |
| show_left = true | 12 | 12 | 5 | 3 | show_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.1http://127.0.0.1:8091/を開きます。初回ビルドでは依存を取得してコンパイルします。データベース、Docker、npm、Reinhardtのサーバーは不要です。動画は編集した説明で、導入時間の計測映像ではありません。
三つ目の値や入れ子の条件を加えてみてください。分岐ごとに依存先を予想してから、値と実行回数の両方を検証します。非アクティブな値で選択表示が再実行された場合は、最小の再現例、ロックファイル、ブラウザーの版、正確なコマンドと出力を残します。貢献ガイドとリアクティブ処理のテストソースが、依存追跡の回帰テストを加える入口になります。
ここで数えたのはEffectのクロージャー呼び出しです。描画時間、ネットワーク遅延、フレームレート、アプリ全体の高速化、本番のスケジューリング動作は測定していません。