4

I simply want to pass a function to one of my children through their props so it can be used there.

Here's the code I have now

use log::info;
use yew::html::onclick::Event;
use yew::prelude::*;


// Create Properties with the function I want to use
#[derive(yew::Properties, PartialEq)]
pub struct MyProps {
    pub do_this: fn(Event) -> (),
    pub val: String,
}

#[function_component(Base)]
pub fn home(props: &MyProps) -> Html {
    let do_this_func = props.do_this.clone();
    html! {
        <button onclick={move |e: Event|  do_this_func(e)}>  {"press me"} </button>
    }
}

// Pass the function
#[function_component(App)]
pub fn app() -> Html {
    fn do_this_func(s: Event) {
        info!("clicked from my func")
    }
    html! {
        <Base do_this={do_this_func} val={"hello".to_string()} />
    }
}

fn main() {
    wasm_logger::init(wasm_logger::Config::default());
    yew::start_app::<App>();
}

If I remove do_this and just pass in val the compiler errors go away. I'd expect that just specifying the type on the props would be enough, but it's not.

Here are the compiler errors I get.

   Compiling yew-app v0.1.0 (/Users/sasacocic/development/tinkering/yew-app)
error[E0277]: the trait bound `fn(MouseEvent) {do_this_func}: IntoPropValue<fn(MouseEvent)>` is not satisfied
  --> src/main.rs:25:24
   |
25 |         <Base do_this={do_this_func} val={"hello".to_string()} />
   |               -------  ^^^^^^^^^^^^ the trait `IntoPropValue<fn(MouseEvent)>` is not implemented for `fn(MouseEvent) {do_this_func}`
   |               |
   |               required by a bound introduced by this call
   |
   = help: the following other types implement trait `IntoPropValue<T>`:
             <&'static str as IntoPropValue<AttrValue>>
             <&'static str as IntoPropValue<Classes>>
             <&'static str as IntoPropValue<Option<AttrValue>>>
             <&'static str as IntoPropValue<Option<String>>>
             <&'static str as IntoPropValue<String>>
             <&T as IntoPropValue<Option<T>>>
             <&T as IntoPropValue<T>>
             <Classes as IntoPropValue<AttrValue>>
           and 6 others
note: required by a bound in `MyPropsBuilder::<MyPropsBuilderStep_missing_required_prop_do_this>::do_this`
  --> src/main.rs:5:10
   |
5  | #[derive(yew::Properties, PartialEq)]
   |          ^^^^^^^^^^^^^^^ required by this bound in `MyPropsBuilder::<MyPropsBuilderStep_missing_required_prop_do_this>::do_this`
6  | pub struct MyProps {
7  |     pub do_this: fn(Event) -> (),
   |         ------- required by a bound in this
   = note: this error originates in the derive macro `yew::Properties` (in Nightly builds, run with -Z macro-backtrace for more info)

I could implement the IntoPropValue trait, but that seems a bit extra for just passing a function to child. Is there an simpler way to do this?

sc3000
  • 121
  • 1
  • 9

1 Answers1

6

A simple solution is to use yew's Callback. Here's how you can do it with the above example.

The code does a few things differently from the above

  1. it does use yew::Callback
  2. it changes fn(Event) -> () in MyProps to Callback<Event>
  3. it creates a Callback by doing Callback::from(do_this_func)
  4. to call the actual function passed it uses emit i.e. do_this_func.emit(e)

the complete code for this is below and commented

use log::info;
use yew::html::onclick::Event;
use yew::prelude::*;
use yew::Callback; // import Callback

#[derive(yew::Properties, PartialEq)]
pub struct MyProps {
    pub do_this: Callback<Event>, // change fn(Event) -> () to Callback<Event>
    pub val: String,
}

#[function_component(Base)]
pub fn home(props: &MyProps) -> Html {
    let do_this_func = props.do_this.clone();

    html! {
        // calls the emit method on the Callback 
        <button onclick={move |e: Event|  do_this_func.emit(e)}>  {"press me"} </button>
    }
}

#[function_component(App)]
pub fn app() -> Html {
    fn do_this_func(s: Event) {
        info!("clicked from my func")
    }


    // creates the callback with Callback::from
    let cb = Callback::from(do_this_func);
    html! {
        <Base do_this={cb} val={"hello".to_string()} />
    }
}

fn main() {
    wasm_logger::init(wasm_logger::Config::default());
    yew::start_app::<App>();
}
sc3000
  • 121
  • 1
  • 9