yew icon indicating copy to clipboard operation
yew copied to clipboard

Tips about component communication

Open Madoshakalaka opened this issue 4 years ago • 2 comments

This is about:

  • Other (Important Tips)

Component communication might deserve a dedicated page.

Ancestor -> Descendant

straightforward: passing props. Using a context if the descendants can be too deep down the tree.

Descedant -> Ancestor

This is not very obvious. In fact, two people asked the same question on Discord in one day. And personally, I had no clue either despite having deployed a Yew app to production (yep it was all Agents & Yewdux....yep I didn't read the docs...yep it was crazy boilerplate...).

The proper way to do this is passing a callback in the props and using Callback::emit in the descendant.

Here's an example of a <Son/> which consumes a callback from the <Dad/>, the <Son/> renders a button, and when the button is clicked, the <Dad/> element will receive a message.

#[function_component(Dad)]
fn dad() -> Html {
    let onclick = Callback::from(|message: String|{
        gloo_console::log!("dad got the message: ", message);
    });
    html! {
        <Son {onclick}></Son>
    }
}


#[derive(Properties, PartialEq)]
pub struct SonProps {
    onclick: Callback<String>
}

#[function_component(Son)]
pub fn son(props: &SonProps) -> Html {

    let onclick = props.onclick.clone();
    let onclick = move |_|{
        onclick.emit("Hi Dad".to_string());
    };

    html! {
        <button {onclick}>{"clicky clicky"}</button>
    }
}

fn main() {
    yew::start_app::<Dad>();
}

Same with the ancestor->descendant direction. When the descendant is too deep down the tree, passing props can lead to too much code. Using context can help. (Should be possible, haven't tried, need code)

In function components, instead of passing callbacks, using use_reducer and passing the UseReducerDispatcher acquired by reducer_handle.dispatcher() should be favored

When the components are not in the same VDom tree

when the components are not in the same Vdom tree for callbacks to be passed around. An Agent should be used (hey, this pub_sub example isn't even mentioned in the docs.)

Alternatives state management tools like yewdux and bounce can also be used. (alright the second one is a bit new but I'm @futursolo shill, no shame)

Madoshakalaka avatar Nov 25 '21 14:11 Madoshakalaka

straightforward: passing props. Using a context if the descendants can be too deep down the tree.

You may find that #905 is related to this topic too.

mc1098 avatar Nov 25 '21 14:11 mc1098

https://github.com/yewstack/yew/pull/2321 adds a section about component communication (see here). It only mentions props for parent to child communication and suggests using contexts for everything else.

As suggested in this issue, we need to expand upon this. A good first step will be to add a section about passing callbacks as props and emiting to them. In that section, we could also mention that this is how we communicate from child to parent.

ranile avatar Jan 01 '22 17:01 ranile

I've added some examples to demonstrate various ways of communicating between components in #2856. If you are happy with the principles, I can spend a bit more time polishing these up and adding documentation.

mibes avatar Sep 01 '22 13:09 mibes

Most of the examples of yew callbacks between child and parent just mention sending some string and just logging it. How can the state of parent be changed based on these callbacks?

Narayanbhat166 avatar Dec 19 '22 13:12 Narayanbhat166

Hi @Narayanbhat166, can point to a particular piece in the example code main.rs that is unclear to you?

The parent's state is changed in lines 29..33, which is triggered by the chid component on line 87/93.

mibes avatar Dec 20 '22 08:12 mibes

I was trying to follow the react architecture, wherein to update the state on receiving callback from the child. This step is neat, I wonder how I missed it.

So in my case, I have a parent component which is controlling a child component. Child will send messages to the parent based on which parent will update the state accordingly.

let callback: Callback<(usize, Message)> = ctx
            .link()
            .callback(|(number, message)| GlobalMessage::ChildMessage(number, message));

and then passing this callback in props to the child component. The child can call this callback with necessary values.

let on_click = props.message.clone();
<button onclick={move |_| on_click.emit((number, Message::Hello))}>Clicked</button>

Thanks

Narayanbhat166 avatar Jan 04 '23 16:01 Narayanbhat166