Skip to content

Regression: elements are re-rendered even if they didn't change, when there's a list below them (worked in v0.19.3) #3262

Open
@andoalon

Description

@andoalon

Problem
Elements are re-rendered even if they didn't change, when there's a list below them. It works correctly in yew 0.19.3, but no longer in yew 0.20.0.

This is especially problematic when the re-rendered element is e.g. a <video> since when it gets re-rendered it stops and resets to the beginning. This is currently blocking me from updating 0.19.3 -> 0.20.0

Steps To Reproduce

  1. Have a component that renders an element and a dynamic list below it
  2. Change the list to cause a re-render of the component
  3. The elements coming from the list get rendered, but so does the other element too (this can be easily seen in the chrome devtools since you can see the element flash when it changes)

Run/see the full repro in this repo: https://github.com/andoalon/yew-bug (just run trunk serve --open)

In short, it boils down to this:

#[function_component(Video)]
pub fn video() -> Html {
    let number = use_state_eq(|| None::<i64>);

    let time_update = {
        let number = number.clone();
        Callback::from(move |event| {
            // number_from_video() returns some number
            // based on the video's current position
            number.set(number_from_video(event));
        })
    };

    let number_vec = {
        if let Some(num) = *number {
            vec![html! {
                <button class="my-button">
                    {format!("Number: {num}")}
                </button>
            }]
        } else {
            Vec::new()
        }
    };

    let number_opt = number.map(|num| html! {
        <button class="my-button">
            {format!("Number: {num}")}
        </button>
    });

    html! {
        <>
            // The video element gets re-rendered when the list below changes
            //  in cases 2, 2.1, 3 and 3.1 (it does not in case 1). It also doesn't get
            // re-rendered in yew 0.19.3
            <video muted=true controls=true type={VIDEO_TYPE} src={VIDEO_URL}
                ontimeupdate={time_update}>
            </video>

            // Case 1: Works
            // if let Some(num) = *number {
            //     <button class="my-button">
            //         {format!("Number: {num}")}
            //     </button>
            // }

            { number_vec } // Case 2: Bug
            //{ for number_vec } // Case 2.1: Bug

            //{ number_opt } // Case 3: Bug
            //{ for number_opt } // Case 3.1: Bug
        </>
    }
}

Expected behavior
Elements are only re-rendered if they changed.

Observed behavior with <video>
It resets to the beginning and stops when the list below it changes. On a different note, I noticed that the muted attribute of <video> seems to not be working. Not sure if I misused it 🤔

Environment:

  • Yew version: v0.20.0 (previously was using v0.19.3 where the issue was not present)
  • Rust version: 1.69.0 (stable)
  • Target, if relevant: wasm32-unknown-unknown (not sure if relevant)
  • Build tool, if relevant: trunk (not sure if relevant)
  • OS, if relevant: Windows 10 (not sure if relevant)
  • Browser and version, if relevant: reproduced in Chrome 112.0.5615.138 (Official Build) (64-bit) and Firefox 112.0.2 (64-bit)

Questionnaire

  • I'm interested in fixing this myself but don't know where to start
  • I would like to fix and I have a solution
  • I don't have time to fix this right now, but maybe later

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions