Nested for-loops in RSX render incorrectly
Problem
Nested for-loops in RSX seem to render in an incorrect order 🤔 This was initially found by @DogeDark on dioxus-web, and I was able to reproduce and simplify the example on dioxus-desktop. Freya however doesn't seem to be affected, so this seems like a post-v0.6 bug.
Looking at the implementation, the for-loop is converted to an iterator of VNodes. My hunch is having the outer iterator call the inner one is leading to the ordering bug but the logic looks correct to me.
https://github.com/DioxusLabs/dioxus/blob/c2b131f249cc757716ca2c4d917cc9b8db98aa08/packages/rsx/src/forloop.rs#L52C31-L52C32
Steps To Reproduce
#[component]
fn App() -> Element {
let mut data = use_signal(|| vec![vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]);
let mut next_value = use_signal(|| 10);
let mut load_more = move || {
let mut new_row = Vec::new();
for _ in 0..10 {
new_row.push(next_value());
next_value += 1;
}
data.push(new_row);
};
rsx! {
button {
onclick: move |_| load_more(),
"load more",
}
div {
style: "display: grid; grid-template-columns: repeat(10, auto);",
for row in data() {
for column in row {
div{ key: "{column}", "{column}" }
}
}
}
}
}
Expected behavior
Separating the above nested loops with a temporary variable results in the following correct output:
Screenshots
However with the nested loop:
Environment:
- Dioxus version: Latest git
- Rust version: Nightly
- App platform: Web + Desktop tested so far (so I think the issue resides in RSX itself)
Questionnaire
- I'm interested in fixing this myself but don't know where to start
If you add many lists to the first render, they render correctly:
use dioxus::prelude::*;
#[component]
fn App() -> Element {
let mut data = use_signal(|| {
(0..100)
.step_by(10)
.map(|i| (i..i + 10).collect::<Vec<_>>())
.collect::<Vec<_>>()
});
let mut load_more = move || {
let mut new_row = Vec::new();
let last = {
let binding = data.last().unwrap();
*binding.last().unwrap()
};
for i in 1..11 {
new_row.push(last + i);
}
data.push(new_row);
};
rsx! {
button {
onclick: move |_| load_more(),
"load more",
}
div {
style: "display: grid; grid-template-columns: repeat(10, auto);",
for (i, row) in data.iter().enumerate() {
for column in row.iter() {
div{ "{column}" }
}
}
}
}
}
fn main() {
launch(App)
}
Diouxs SSR doesn't use core diffing and it also renders the lists correctly. I think this is a bug with keyed list diffing (here). If it was a bug with rsx, then the initial list would render incorrectly as well