DioxusLabs / dioxus

Fullstack app framework for web, desktop, mobile, and more.
https://dioxuslabs.com
Apache License 2.0
20.32k stars 779 forks source link

bug: Gradual slowdown when reading signals in callbacks passed as props #1861

Closed marc2332 closed 8 months ago

marc2332 commented 8 months ago

I have tested this in https://github.com/DioxusLabs/dioxus/pull/1791, but I don't know if it also affects develop.

It seems to be caused because of reading in L29, and somehow L18 "fixes" temporarily the issue until it starts slowing down again

Code:

#![allow(non_snake_case)]
use std::{rc::Rc, time::Duration};

use dioxus::prelude::*;
use dioxus_core::NoOpMutations;
use tokio::time::{sleep, Instant};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    fn app() -> Element {
        let values = use_signal(|| ["Hello, World!"].repeat(800));
        let schedule_update = schedule_update();

        use_hook(|| {
            spawn(async move {
                loop {
                    // This "fixes" the slowdown temporarily
                    sleep(Duration::from_secs(10)).await;
                    schedule_update();
                    println!("Again!");
                }
            });
        });

        rsx!(VirtualScrollView {
            length: values.read().len(),
            builder: Rc::new(move |index| {
                // Uncomment the L28 and comment the L29 and it will not lag
                // let value = "Hello, World";
                let value = values.peek()[index];
                rsx!(
                    p {
                        key: "{index}",
                        height: "25",
                        width: "100%",
                        "{index} {value}"
                    }
                )
            })
        })
    }

    #[derive(Props, Clone)]
    pub struct VirtualScrollViewProps {
        pub length: usize,
        pub builder: Rc<dyn Fn(usize) -> Element>,
    }

    impl PartialEq for VirtualScrollViewProps {
        fn eq(&self, other: &Self) -> bool {
            self.length == other.length && Rc::ptr_eq(&self.builder, &other.builder)
        }
    }

    #[allow(non_snake_case)]
    pub fn VirtualScrollView(props: VirtualScrollViewProps) -> Element {
        let mut window = use_signal(|| (0..50));

        if window.read().end >= props.length {
            window.set(0..50);
        } else {
            spawn(async move {
                sleep(Duration::from_millis(1)).await;
                window.with_mut(|v| *v = v.start + 50..v.end + 50);
            });
        }

        let children = window.read().clone().map(|i| (props.builder)(i));

        rsx!({ children })
    }

    tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()?
        .block_on(async {
            let mut vdom = VirtualDom::new(app);

            let _ = vdom.rebuild(&mut NoOpMutations);
            loop {
                let mut now = Instant::now();
                // wait for the vdom to update
                vdom.wait_for_work().await;

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

                println!("{}", now.elapsed().as_millis());
            }
        })
}
ealmloff commented 8 months ago

Thanks for the bug report. This should be fixed now. We were not properly removing the borrow location when dropping a ref from read/peek.

Fixed by https://github.com/DioxusLabs/dioxus/pull/1791/commits/93adb35cfa9b6e9f006c028803d801b871e7236b