pub trait Component: 'static {
// Required methods
fn view(&self, ctx: &Context) -> Node;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
// Provided methods
fn update(
&self,
ctx: &Context,
msg: Box<dyn Message>,
topic: Option<&str>,
) -> Action { ... }
fn effects(&self, _ctx: &Context) -> Vec<Effect> ⓘ { ... }
fn type_id(&self) -> TypeId { ... }
}Expand description
Main component trait for building UI components
Components can be created easily using the #[derive(Component)] macro.
The update, view, and effects methods can be simplified using attribute macros
which automatically handle message downcasting, state fetching, and effect collection.
§Basic Example
ⓘ
use rxtui::prelude::*;
// Components can be unit structs or structs with fields
#[derive(Component)]
struct Counter;
// Or with fields:
// #[derive(Component)]
// struct Counter {
// initial_value: i32,
// }
impl Counter {
// Using the #[update] macro - handles downcasting and state automatically
#[update]
fn update(&self, ctx: &Context, msg: CounterMsg, mut state: CounterState) -> Action {
match msg {
CounterMsg::Increment => {
state.count += 1;
Action::update(state)
}
CounterMsg::Decrement => {
state.count -= 1;
Action::update(state)
}
}
}
// Using the #[view] macro - automatically fetches state
#[view]
fn view(&self, ctx: &Context, state: CounterState) -> Node {
node! {
div [
text(format!("Count: {}", state.count))
]
}
}
}§With Async Effects (using #[component] macro)
The #[component] macro automatically collects all #[effect] methods:
ⓘ
use rxtui::prelude::*;
use std::time::Duration;
#[derive(Component)]
struct Timer;
#[component] // This macro handles effect collection
impl Timer {
#[update]
fn update(&self, ctx: &Context, msg: TimerMsg, mut state: TimerState) -> Action {
match msg {
TimerMsg::Tick => {
state.seconds += 1;
Action::update(state)
}
TimerMsg::Reset => {
state.seconds = 0;
Action::update(state)
}
}
}
#[view]
fn view(&self, ctx: &Context, state: TimerState) -> Node {
node! {
div [
text(format!("Time: {}s", state.seconds))
]
}
}
// Mark async methods as effects - they'll be auto-collected
#[effect]
async fn tick_timer(&self, ctx: &Context) {
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
ctx.send(TimerMsg::Tick);
}
}
// Can have multiple effects, with optional state access
#[effect]
async fn monitor_state(&self, ctx: &Context, state: TimerState) {
// State is automatically fetched via ctx.get_state()
if state.seconds > 60 {
ctx.send(TimerMsg::Reset);
}
}
}§Manual Implementation
The trait can also be implemented manually for more control:
ⓘ
fn update(&self, ctx: &Context, msg: Box<dyn Message>, topic: Option<&str>) -> Action {
if let Some(msg) = msg.downcast::<MyMsg>() {
// Handle message
}
Action::none()
}
fn view(&self, ctx: &Context) -> Node {
let state = ctx.get_state::<MyState>();
// Build UI
}
fn effects(&self, ctx: &Context) -> Vec<Effect> {
vec![
Box::pin({
let ctx = ctx.clone();
async move {
// Async effect logic
}
})
]
}Required Methods§
fn view(&self, ctx: &Context) -> Node
fn as_any(&self) -> &dyn Any
fn as_any_mut(&mut self) -> &mut dyn Any
Provided Methods§
fn update( &self, ctx: &Context, msg: Box<dyn Message>, topic: Option<&str>, ) -> Action
Sourcefn effects(&self, _ctx: &Context) -> Vec<Effect> ⓘ
fn effects(&self, _ctx: &Context) -> Vec<Effect> ⓘ
Define effects for this component
Effects are async tasks that run outside the main event loop. They are spawned when the component mounts and cancelled when it unmounts.
§Using the #[component] and #[effect] macros (Recommended)
The easiest way is to use the #[component] macro on your impl block
and mark async methods with #[effect]:
ⓘ
#[component]
impl MyComponent {
#[effect]
async fn background_task(&self, ctx: &Context) {
// Async work here
}
}§Manual Implementation
You can also implement this method manually:
ⓘ
fn effects(&self, ctx: &Context) -> Vec<Effect> {
vec![
Box::pin({
let ctx = ctx.clone();
async move {
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
ctx.send(MyMsg::Tick);
}
}
})
]
}§Common Use Cases
- Timers: Periodic updates (e.g., clocks, progress bars)
- Network requests: Fetching data from APIs
- File watching: Monitoring file system changes
- WebSocket connections: Real-time communication
- Background calculations: Heavy computations that shouldn’t block UI