dioxus icon indicating copy to clipboard operation
dioxus copied to clipboard

Diffing bug

Open elias098 opened this issue 10 months ago • 2 comments

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:

  • Dioxus version: 0.51
  • Rust version: 1.76.0
  • OS info: MacOS
  • App platform: Web

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

elias098 avatar Apr 17 '24 03:04 elias098

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.

jkelleyrtp avatar Apr 17 '24 10:04 jkelleyrtp

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

ealmloff avatar Apr 17 '24 14:04 ealmloff

I can no longer reproduce the issue with this code on the main branch of dioxus:

use dioxus::prelude::*;
fn main() {
    tracing_subscriber::fmt::init();
    launch(app);
}

#[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(|| tracing::info!("{test}, {b}, {c}"));
    rsx! {
        {test().to_string()}
        {b.to_string()}
        {c.to_string()}
    }
}

ealmloff avatar Jul 30 '24 19:07 ealmloff