Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tips about component communication #2197

Closed
Tracked by #2753
Madoshakalaka opened this issue Nov 25, 2021 · 9 comments · Fixed by #2856
Closed
Tracked by #2753

Tips about component communication #2197

Madoshakalaka opened this issue Nov 25, 2021 · 9 comments · Fixed by #2856

Comments

@Madoshakalaka
Copy link
Contributor

Madoshakalaka commented Nov 25, 2021

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)

@mc1098
Copy link
Contributor

mc1098 commented Nov 25, 2021

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.

@ranile
Copy link
Member

ranile commented Jan 1, 2022

#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.

@mibes
Copy link
Contributor

mibes commented Sep 1, 2022

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.

@Narayanbhat166
Copy link

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?

@mibes
Copy link
Contributor

mibes commented Dec 20, 2022

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.

@Narayanbhat166
Copy link

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

@Bowarc
Copy link

Bowarc commented Jul 6, 2024

While making a notification system, i was wondering if child to child communication was possible, ideally i don't want to store the notifications in the parent component.

@WorldSEnder
Copy link
Member

@Bowarc this should be possible if the parent/common ancestor plays along and creates/sets up and shares a channel with both via properties/a context.

@Bowarc
Copy link

Bowarc commented Aug 31, 2024

@Bowarc this should be possible if the parent/common ancestor plays along and creates/sets up and shares a channel with both via properties/a context.

I went with the (rather ugly) approach of using a static RefCell<Option<Callback<Notification>>>

And it seems to be working flawlessly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants