DioxusLabs / dioxus

Fullstack GUI library for web, desktop, mobile, and more.
https://dioxuslabs.com
Apache License 2.0
18.5k stars 703 forks source link

Diffing bug #2324

Open elias098 opened 2 weeks ago

elias098 commented 2 weeks ago

Problem

https://discord.com/channels/899851952891002890/943190605067079712/1229957121496580197

With the below code it recreates the ListTest component when ListTest2 goes back to its initial state. As in if it started with a % 3 == 0 then it recreates it every time a % 3 == 0

Steps To Reproduce

use dioxus::prelude::*;

#[component]
pub fn App() -> Element {
    let mut b = use_signal(|| 0);

    rsx! {
        ListTest2 {
            a: b()
        }
        button {
            onclick: move |_| b += 1,
            "+"
        }
        button {
            onclick: move |_| b -= 1,
            "-"
        }
    }
}

#[component]
pub fn ListTest2(a: u32) -> Element {
    rsx! {
        if a % 3 == 0 {
            ListTest {
                test: 4u32,
                b:4u32,
                c:6u32
            }
        } else if a % 3 == 1 {
            ListTest {
                test: 5u32,
                b:6u32,
                c:6u32
            }
        } else {
            ListTest {
                test: 3u32,
                b:3u32,
                c:6u32
            }
        }
    }
}

#[component]
pub fn ListTest(test: ReadOnlySignal<u32>, b: u32, c: u32) -> Element {
    use_hook(|| log::info!("{test}, {b}, {c}"));
    rsx! {
        {test().to_string()}
        {b.to_string()}
        {c.to_string()}
    }
}

Expected behavior

The way i had it explained to me is that it shouldnt recreate the component at all with the above code

Environment:

Questionnaire

jkelleyrtp commented 2 weeks ago

Right now we do swap templates completely depending on the unique ID of every macro call. I believe there's a comment in the codebase to that effect too, though in this particular instance tossing a few checks in wouldn't terribly large overhead so we could support this usecase.

You could, if you wanted, drive the props from the conditional and pass it into a single rsx! call which won't swap templates between renders.

ealmloff commented 2 weeks ago

We don't guarantee this behavior, but I think this should be handled by light_diff_template. That code doesn't seem to be working correctly