0

I am starting to learn Rust with Yew. I followed the counter example in the documentation and now I am trying to implement an input functionality. I want to show the input value in html. I am able to console.log it but not to render it in the browser.

What am I missing?

This is the code I have so far:

use wasm_bindgen::{JsCast, UnwrapThrowExt};
use web_sys::HtmlInputElement;
use yew::prelude::*;

#[function_component]
fn App() -> Html {
    let name = use_state(|| "");
    let oninput = Callback::from(move |input_event: InputEvent| {
        let name = name.clone();
        let target: HtmlInputElement = input_event
            .target()
            .unwrap_throw()
            .dyn_into()
            .unwrap_throw();
        //web_sys::console::log_1(&target.value().into()); // <- can console the value.
        move |_: HtmlInputElement| name.set(&target.value().as_str());
    });

    html! {
        <div>
            <input {oninput}  />
            <p>{"name: "}<h5>{name}</h5></p> // <-- here is the error
        </div>
    }
}

fn main() {
    yew::Renderer::<App>::new().render();
}

But I get this error:

error[E0277]: `UseStateHandle<&str>` doesn't implement `std::fmt::Display`
  --> src/main.rs:22:29
   |
22 |             <p>{"name"}<h5>{name}</h5></p>
   |                             ^^^^
   |                             |
   |                             `UseStateHandle<&str>` cannot be formatted with the default formatter
   |                             required by a bound introduced by this call
   |
   = help: the trait `std::fmt::Display` is not implemented for `UseStateHandle<&str>`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
   = note: required for `UseStateHandle<&str>` to implement `ToString`
   = note: required for `VNode` to implement `From<UseStateHandle<&str>>`
   = note: required for `UseStateHandle<&str>` to implement `Into<VNode>`
   = note: 2 redundant requirements hidden
   = note: required for `UseStateHandle<&str>` to implement `Into<NodeSeq<UseStateHandle<&str>, VNode>>`

I tried to use name.get(), &name, name.clone() and combination of those but I always get an error. Could you explain me why I cannot get the value to show in the browser?

I appreciate any help.

programandoconro
  • 2,378
  • 2
  • 18
  • 33

1 Answers1

3

To use the value in the state, you need to dereference it:

<p>{"name: "}<h5>{*name}</h5></p>

However, now you will see another errors:

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:23:46
   |
16 |         let name = name.clone();
   |             ---- lifetime `'1` appears in the type of `name`
...
23 |         move |_: HtmlInputElement| name.set(&target.value().as_str());
   |                                    ----------^^^^^^^^^^^^^^----------
   |                                    |         |                      |
   |                                    |         |                      temporary value is freed at the end of this statement
   |                                    |         creates a temporary value which is freed while still in use
   |                                    argument requires that borrow lasts for `'1`

error[E0382]: borrow of moved value: `name`
   --> src/main.rs:29:31
    |
14  |     let name = use_state(|| "");
    |         ---- move occurs because `name` has type `UseStateHandle<&str>`, which does not implement the `Copy` trait
15  |     let oninput = Callback::from(move |input_event: InputEvent| {
    |                                  ------------------------------ value moved into closure here
16  |         let name = name.clone();
    |                    ---- variable moved due to use in closure
...
29  |             <p>{"name: "}<h5>{*name}</h5></p> // <-- here is the error
    |                               ^^^^^ value borrowed here after move
    |
    = note: borrow occurs due to deref coercion to `&str`
note: deref defined here
   --> /home/explorer/.cargo/registry/src/github.com-1ecc6299db9ec823/yew-0.20.0/src/functional/hooks/use_state.rs:128:5
    |
128 |     type Target = T;
    |     ^^^^^^^^^^^

The first error is because you need to store String and not &str. The second is because you need to clone() the state before the closure:

#[function_component]
fn App() -> Html {
    let name = use_state(|| String::new());
    let oninput = Callback::from({
        let name = name.clone();
        move |input_event: InputEvent| {
            let target: HtmlInputElement = input_event
                .target()
                .unwrap_throw()
                .dyn_into()
                .unwrap_throw();
            //web_sys::console::log_1(&target.value().into()); // <- can console the value.
            name.set(target.value());
        }
    });

    html! {
        <div>
            <input {oninput}  />
            <p>{"name: "}<h5>{&*name}</h5></p>
        </div>
    }
}
Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77