0

I am making a web app using Yew Rust as front end. The structs User, Post, Vote are wrappers over JSON objects from the backend. I want to request several PostCards from the backend, format them using the html! macro and render them.

The problem is that it always renders the first element (even if I have 5 elements in self.post_cards, the first one is rendered 5 times). Iterating over self.post_cards before the html! and logging data about the postcard, I see that the data is different (the same as the data sent from the backend, so JSON parsing works).

This is how I add data to self.post_cards:

#[derive(Clone)]
pub struct Index {
    post_cards: Vec<PostCard>,
    can_request: bool,
    current_index: u32,
}

...

if let Value::Array(v) = &json["data"] {
    for value in v {
        self.post_cards.push(PostCard::from_json(value.clone()));
    }
}

View method:

fn view(&self, ctx: &Context<Self>) -> Html {
    html! {
    <>
      <Navbar/>
      <div>
      {
            self.post_cards.iter().map(|pc| {
                html!{
                    <PostCard
                        post={pc.post.clone()}
                        user={pc.user.clone()}
                        vote={pc.vote.clone()}
                    />
                }
            }).collect::<Html>()
      }
      </div>
      <Footer/>
    </>
    }
}
#[derive(Clone, PartialEq)]
pub struct PostCard {
    pub post: Post,
    pub user: User,
    pub vote: Vote,
}

I am using yew 0.20.0 and rustc 1.67.1

I also tried a more hard coded approach: to render the first item if the size is >0, render the second item if the size is >1 and it worked, but I would rather not use this approach as the postcard count can get pretty big.

Edit 1: It seems that the PostCard component doesn't properly create new items. By adding a log in html! and PostCard create() method I saw that the values were different.

self.post_cards.iter().map(|pc| {
    log!("index.rs html!", pc.post.clone().text);
    html!{
        <PostCard
            post={pc.post.clone()}
            user={pc.user.clone()}
            vote={pc.vote.clone()}
        />
    }
}).collect::<Html>()

impl Component for PostCard {
...
fn create(ctx: &Context<Self>) -> Self {
    log!("=== PostCard created === ");
    log!("PostCard post text\n", ctx.props().post.text.clone());
    PostCard {
        post: ctx.props().post.clone(),
        user: ctx.props().user.clone(),
        vote: ctx.props().vote.clone(),
    }
}
...
}

So the issue now is with the way each element is created, is there a different way of doing this?

Edit 2: I added the changed() method to the PostCard struct and now everything works as intended, however is this the correct way to render vectors of components?

fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
    self.post = ctx.props().post.clone();
    self.user = ctx.props().user.clone();
    self.vote = ctx.props().vote.clone();
    true
}
radu781
  • 3
  • 2
  • Can you please [edit] your post and include a [mre] I can't reproduce the error when extending your snippets to something runnable. – cafce25 Apr 16 '23 at 09:36
  • @cafce25 I added some clarification, hope this is enough to tell where the problem is, otherwise I will make a minimal reproductible example – radu781 Apr 16 '23 at 10:36

0 Answers0