Component

Trait Component 

Source
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§

Source

fn view(&self, ctx: &Context) -> Node

Source

fn as_any(&self) -> &dyn Any

Source

fn as_any_mut(&mut self) -> &mut dyn Any

Provided Methods§

Source

fn update( &self, ctx: &Context, msg: Box<dyn Message>, topic: Option<&str>, ) -> Action

Source

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.

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
Source

fn type_id(&self) -> TypeId

Get the TypeId of this component for identity tracking

Implementors§