DioxusLabs / dioxus

App framework for web, desktop, mobile, and more.
https://dioxuslabs.com
Apache License 2.0
20.43k stars 786 forks source link

bug(memory leak): Event handlers passed as props are not properly dropped #2272

Closed marc2332 closed 5 months ago

marc2332 commented 5 months ago

Problem

#![allow(non_snake_case)]
use dioxus::prelude::dioxus_core::NoOpMutations;
use dioxus::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    fn app() -> Element {
        let mut count = use_signal(|| 0);

        use_hook(|| spawn(async move {
            loop {
                tokio::time::sleep(std::time::Duration::from_nanos(5)).await;
                let val = *count.peek_unchecked();
                if val == 70 {
                    count.set(0);
                } else {
                    count.set(val + 1);
                }
            }
        }));

        rsx! {
            div { 
                color: "red",
                onwheel: move |_| { println!("wheel"); },
                for el in 0..*count.read() {
                    div {
                        key: "{el}",
                        Comp {
                            onclick: move |_| { println!("click"); },
                        }
                        Comp {
                            onclick: move |_| { println!("click"); },
                        }
                        Comp {
                            onclick: move |_| { println!("click"); },
                        }
                        Comp {
                            onclick: move |_| { println!("click"); },
                        }
                        Comp {
                            onclick: move |_| { println!("click"); },
                        }
                        Comp {
                            onclick: move |_| { println!("click"); },
                        }

                    }
                }
            }
        }
    }

    #[component]
    fn Comp(onclick: EventHandler<()>) -> Element {
        rsx! {
            div { 
                onclick: move |_| onclick.call(()),
            }
        }
    }

    // create the vdom, the real_dom, and the binding layer between them
    let mut vdom = VirtualDom::new(app);

    vdom.rebuild(&mut NoOpMutations);

    // we need to run the vdom in a async runtime
    tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()?
        .block_on(async {
            loop {
                // wait for the vdom to update
                vdom.wait_for_work().await;

                // get the mutations from the vdom
                 vdom.render_immediate(&mut NoOpMutations);
            }
        })
}

Expected behavior

Not leak

Screenshots

after running for some seconds

image

Environment: