yewstack / yew

Rust / Wasm framework for creating reliable and efficient web applications
https://yew.rs
Apache License 2.0
30.61k stars 1.42k forks source link

Individual item render order is reversed #3673

Open Cationiz3r opened 3 months ago

Cationiz3r commented 3 months ago

Problem

The numbers are arranged in the correct order (from 1 to 5) after being rendered, but the order in which each is rendered, reported by gloo_console::debug!, are reversed (from 5 to 1).

I currently maintain a file managing application that display files in a grid. When a directory contains images, their thumbnails will be rendered as img elements, but in the aforementioned order. Because the browser sees the last element first, it also downloads and displays image data from last to first. Over a weak internet connection, this becomes really noticeable, which is undesirable.

Steps To Reproduce Run the following code (with trunk).

use yew::prelude::*;

#[derive(Debug, PartialEq, Properties)]
struct Properties {
    pub num: i32,
}

#[function_component]
fn Number(props: &Properties) -> Html {
    let num = props.num;
    gloo_console::debug!(format!("Render: {}", num)); // DEBUG:
    html! { <div>{ props.num }</div> }
}

#[function_component]
fn App() -> Html {
    let numbers = 1..=5;
    numbers.map(|num| html! { <Number {num} /> }).collect()
}

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

Expected behavior I expect the debug messages to appear in the same order as the numbers.

Screenshots Taken from the browser web console (Ctrl+Shift+K).

240619_131725

Environment:

Questionnaire

WorldSEnder commented 1 month ago

Depending on the exact render-order of each item sounds like a not-so-good idea. I agree that it is unintuitive to see the specific components render in opposite order, but from a contract perspective, yew does so far not guarantee any order, they might also render "randomly" or not even in a consistent order. This has to do with the order in which we traverse the component tree and we might change it at any time, due to e.g. changing the format in which we render SSR and other details.

To give a potential solution to this problem: You might get away with add replacement images by first rendering a placeholder with inline data, such as (you can probably golf this a little and have better size constraints)

data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgMzUwIiB3aWR0aD0iNjAwIiBoZWlnaHQ9IjM1MCI+CiAgPHJlY3Qgd2lkdGg9IjYwMCIgaGVpZ2h0PSIzNTAiIGZpbGw9IiNjY2NjY2MiPjwvcmVjdD4KICA8dGV4dCB4PSI1MCUiIHk9IjUwJSIgZG9taW5hbnQtYmFzZWxpbmU9Im1pZGRsZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9Im1vbm9zcGFjZSIgZm9udC1zaXplPSIyNnB4IiBmaWxsPSIjMzMzMzMzIj42MDB4MzUwPC90ZXh0PiAgIAo8L3N2Zz4=

and then, after you initially rendered in an use_effect, replace the src with your actual image src but trigger it only when your image is actually on screen or close to being relevant. This should already noticably improve your UX. I suspect that in your use case, these effects might again trigger in an undesirable order for items that are initially on screen. Perhaps you can slightly delay setting the src and fix the order by collecting into a buffer in the use_effect and wasm_bindgen_futures::spawn_local` the part that drains this buffer (in your desired order) and sets the src.

Again, this sounds like a work-around, but as a framework we try to not commit to a specific render order. While unintuitive, the problem is not super straight-forward to answer in any case once you try to argue for post/pre/in-order or depth-first/breadth-first creation of component hierarchies (keep in mind that the creation of the actual DOM and the calls to the view function which returns virtual DOM might also not happen in the same order. As such, your debug statement in the component's view function does not necessarily capture the order in which the <img> elements are created).