Sub Components

Components wouldnt be very useful if we could not compose them. Due to trait limitations we cant use components as Elements directly. But there is a simple C wrapper to facilitate this.

extern crate natrix;
use natrix::prelude::*;

#[derive(Component)]
struct MyChild {
    /* Any State You Want */
}

impl Component for MyChild {
    fn render() -> impl Element<Self> {
        /* Your Component */
        e::div()
    }
}

#[derive(Component)]
struct MyParent {
    /* Any State You Want */
}

impl Component for MyParent {
    fn render() -> impl Element<Self> {
        e::div()
            .child(C::new(MyChild {
                /* Initial Child State */
            }))
    }
}

Message Passing

A common requirement is communication between components. This is where the EmitMessage and ReceiveMessage associated types come in. These are used to declare what type is used for message passing to and from the component. The NoMessages type is a enum with no variants (i.e similar to Infallible) and is used when you do not need to pass messages.

Child to Parent

Define the EmitMessage type to the type of the message you will be emitting and then use ctx.emit, you can then use .on to listen for the message in the parent component.

extern crate natrix;
use natrix::prelude::*;
#[derive(Component)]
struct MyChild;

impl Component for MyChild {
    type EmitMessage = usize;

    fn render() -> impl Element<Self> {
        e::button()
            .text("Click Me")
            .on::<events::Click>(|ctx: E<Self>, _| {
                ctx.emit(10);
            })
    }
}

#[derive(Component)]
struct MyParent {
    state: usize,
};

impl Component for MyParent {
    fn render() -> impl Element<Self> {
        e::div()
            .child(C::new(MyChild).on(|ctx: E<Self>, msg| {
                ctx.state += msg;
            }))
    }
}

Parent to Child

Similaryly you can use ReceiveMessage to listen for messages from the parent component. You overwrite the default handle_message method to handle the message. In the parent you use .sender to get a sender for the child component.

extern crate natrix;
use natrix::prelude::*;
#[derive(Component, Default)]
struct MyChild {
    state: usize,
}

impl Component for MyChild {
    type ReceiveMessage = usize;

    fn render() -> impl Element<Self> {
        e::div()
            .text(|ctx: R<Self>| *ctx.state)
    }

    fn handle_message(ctx: E<Self>, msg: Self::ReceiveMessage) {
        *ctx.state += msg;
    }
}

#[derive(Component)]
struct MyParent;

impl Component for MyParent {
    fn render() -> impl Element<Self> {
        let (child, sender) = C::new(MyChild::default()).sender();
        e::div()
            .child(child)
            // We use `move` to move ownership of the sender into the closure
            .on::<events::Click>(move |ctx: E<Self>, _| {
                sender.send(10);
            })
    }
}

As you see this generally requires you to use a let binding to split the return of .sender. The Sender is also cloneable.

When do messages get processed?

Messages passing uses async channels internally, this means the messages will be processed once the current components reactivity cycle is finished. This will still run before the next reflow of the browser, and all messages are batched for efficiency.