Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Yew: Difficulty with nested callbacks

Tags:

callback

rust

yew

I'm attempting to do something that I feel is pretty basic: I have a pulldown, and I'd like the onchange event for that pulldown to cause the program to fetch some data from the backend based on the user's input. (And then, you know, give the user more options based on the first thing they picked. Really simple, and seems like I ought to have been able to find an easy way to do this.)

Full code for this minimal (failing) example is at: https://github.com/djmcmath/broken-yew

But the relevant bit, which doesn't behave correctly, is below:

  • The view function renders, delightfully, an iterated list. I pass in a callback, so it knows what to do on the "onchange" event.
  • The callback gets executed, which makes me very happy. But it isn't calling the Msg::GetData. This compiles, which is nice, but it doesn't work, which is less nice.

I've spent, I'm ashamed to admit, several weeks of my spare time fighting with this. I think it has something to do with scopes and lifetimes. I think that the way I'm making this compile -- by cloning the context and using "move" disconnects it from the actual context that I need to make this work. But every variation on the theme that I've been able to find in examples and references complains about scope or lifetimes.

Thanks in advance for the help.

    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool { 
        match msg {
            Msg::GetData(value) => {
                log::info!("Start 'fetch' with user-selected value: {}", value);
                ctx.link().send_future(async {
                    match fetch_markdown("url_shortened_for_clarity").await {
                        Ok(md) => Msg::SetMarkdownFetchState(FetchState::Success(md)),
                        Err(err) => Msg::SetMarkdownFetchState(FetchState::Failed(err)),
                    }
                });
                false
            },
            Msg::SetMarkdownFetchState(fetch_state) => {
                let mut wr = WebReturn { term_id: 0, dow: 0, dep_time_num: 0 };
                match fetch_state {
                    FetchState::Success(s) => { wr = serde_json::from_str(&s).expect(&format!("Poorly formatted JSON! {}", s).to_string()); },
                    FetchState::Failed(f) => { log::info!("Fetch failed: {}", f); },
                    FetchState::NotFetching => {},
                    FetchState::Fetching => {}
                };
            
                log::info!("term_id (3) : {}, dep_time_num (12000) : {}, and dow (3) : {}", wr.term_id, wr.dep_time_num, wr.dow);
                true
            }
        }
    }

    fn view(&self, ctx:&Context<Self>) -> Html {
        let ctx_link = ctx.link().clone();
        let my_callback: Callback<String> = Callback::from(move |value: String| {
            let val_as_num = value.parse::<i32>().unwrap_or(0);
            log::info!("Returned value: {}", val_as_num);
            ctx_link.callback(|val_as_num: i32| Msg::GetData(val_as_num));
        });
        
        html! {
            <div>
                { self.render_list(&self.props.term_list, my_callback) }
            </div>
        }
    }
like image 951
djmcmath Avatar asked Dec 29 '25 19:12

djmcmath


1 Answers

This line does not "call back" to your component, it creates a callback and then doesn't call it:

ctx_link.callback(|val_as_num: i32| Msg::GetData(val_as_num));

You need to instead call .send_message() in your callback or, better yet, create your original callback with .callback():

let my_callback = ctx_link.callback(|value: String| {
    let val_as_num = value.parse::<i32>().unwrap_or(0);
    log::info!("Returned value: {}", val_as_num);
    Msg::GetData(val_as_num)
});
like image 87
kmdreko Avatar answered Jan 01 '26 20:01

kmdreko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!