dioxus icon indicating copy to clipboard operation
dioxus copied to clipboard

`document::eval` inside a `use_drop` fails assertion in the presence of another eval

Open jmsfltchr opened this issue 10 months ago • 1 comments

Problem

Steps To Reproduce

Steps to reproduce the behavior:

Use a document::eval in a spawn in a use_drop

use_drop(move || { // Also for use_effect
    spawn(async move {
        let _ = document::eval(&*format!(r#"
        ...some JS...
        "#)).await.expect("Failed");  // .awating the eval causes the panic
    });
});

Plus in my root component I had:

use_effect(move || {
         spawn(async move {
         let _ = document::eval("...Some JS...").await;
    });
});

The effect of the two combined was to fail an internal assertion in dioxus:

thread 'main' panicked at /Users/jamesfletcher/.cargo/git/checkouts/dioxus-1e619ce595d3799d/66e284c/packages/core/src/scheduler.rs:155:9:
assertion failed: self.scopes.contains(tasks.order.id.0)

The assertion that was offended:

    pub(crate) fn pop_task(&mut self) -> Option<Task> {
        let mut dirty_tasks = self.runtime.dirty_tasks.borrow_mut();
        let tasks = dirty_tasks.first()?;

        // The scope that owns the effect should still exist. We can't just ignore the task if the scope doesn't exist
        // because the scope id may have been reallocated
        debug_assert!(self.scopes.contains(tasks.order.id.0));

Removing either the use_effect or the use_drop avoided the assertion.

The fix was to use spawn_forever inside the use_drop. Surely there is no case where spawn is warranted in a use_drop, so perhaps this should be listed in the docs somewhere. It's not as generic as an antipattern, but that's the closest place I can think of that fits. Note that as a user fairly well-versed in the docs on the website, but less so from docs.rs, I has no idea spawn_forever was a thing!

Expected behavior

No assertion error

Screenshots

N/A

Environment:

  • Dioxus version: main, desktop, 66e284ce42e381d1d665f1c983f18b0529dbcba8
  • Rust version: 1.84.0
  • OS info: macOS
  • App platform:

Questionnaire

jmsfltchr avatar Apr 30 '25 09:04 jmsfltchr

Could you share a reproduction of this issue? I can't reproduce the issue on the current main branch of dioxus with this code:

// dioxus = { path = "..", features = ["desktop"] }
use dioxus::prelude::*;

#[component]
fn App() -> Element {
    use_effect(move || {
        spawn(async move {
            let _ = document::eval("await fetch('https://example.com')").await;
        });
    });
    let mut toggle = use_signal(|| false);
    rsx! {
        button {
            onclick: move |_| {
                toggle.toggle();
            },
            "Toggle"
        }
        if toggle() {
            Other {}
        }
    }
}

#[component]
fn Other() -> Element {
    use_drop(move || {
        // Also for use_effect
        spawn(async move {
            let _ = document::eval(&*format!(
                r#"await fetch('https://example.com')"#
            ))
            .await
            .expect("Failed"); // .awating the eval causes the panic
        });
    });
    rsx! {
        h1 { "Home" }
    }
}

fn main() {
    dioxus::LaunchBuilder::new().launch(App);
}

ealmloff avatar May 27 '25 18:05 ealmloff