本文へスキップ

リアクティビティ

状態は View に保持します。render はその状態を読んで要素を組み立てます。変更したあとに cx.invalidate() を呼び出すと、ウィンドウが再描画されます。

#リアクティブな状態

フィールドはビューの構造体に置きます。イベントがそれらのフィールドを変更できるよう、cx.listener でリスナーを登録します。

struct Counter {
    count: usize,
}
impl View for Counter {
    fn render(&mut self, cx: &mut ViewContext<'_, Self>) -> impl IntoElement {
        let increment = cx.listener("increment", |this, cx: &mut EventContext| {
            this.count += 1;
            cx.invalidate();
        });
        div()
            .child(text(format!("Count: {}", self.count)))
            .child(button().on_click(increment).child("Increment"))
    }
}

render でコピーした値は、そのフレーム時点のスナップショットです。

#派生状態

すでに保持しているフィールドから、render 内で派生値を計算します。

let doubled = self.count * 2;
text(format!("{doubled}"))

同じフレームで複数の子が同じ値を必要とするときは、コストの高い派生結果をビューに保持します。

#副作用とクリーンアップ

外部の処理はリスナーまたは Application のコールバックから開始し、ビューがドロップされたときに止めます。ウィンドウを閉じると、ビューとそのリスナーがドロップされます。

impl Drop for WatcherView {
    fn drop(&mut self) {
        self.stop.take().map(|stop| stop());
    }
}

#更新のバッチ処理

cx.invalidate() を呼び出すと、ウィンドウは再描画が必要だとマークされます。1 つのイベント内の複数の無効化は、1 回の描画になります。何も変わっていないときに再描画するタイマーは置かないでください。