`document::eval` inside a `use_drop` fails assertion in the presence of another eval
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
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);
}