Context is not provided to event handlers in inline component children
Problem
This is a spin-off from #4011
The ErrorBoundary construction seems entirely broken.
Steps To Reproduce
use dioxus::prelude::*;
const FAVICON: Asset = asset!("/assets/favicon.ico");
const MAIN_CSS: Asset = asset!("/assets/main.css");
fn main() {
dioxus::launch(App);
}
#[component]
fn App() -> Element {
rsx! {
document::Link { rel: "icon", href: FAVICON }
document::Link { rel: "stylesheet", href: MAIN_CSS }
Hero {}
}
}
#[component]
pub fn TryToCatchError(title: String) -> Element {
rsx! {
ErrorBoundary {
handle_error: |_| {
// We would expect to see this (we do not)
rsx! { "WORKS" }
},
button {
// Simulate some kind of failure happening
onclick: move |_| {
Err(std::io::Error::new(std::io::ErrorKind::Other, "oh no!"))?;
Ok(())
},
"{title}"
}
}
}
}
#[component]
pub fn Hero() -> Element {
rsx! {
div {
id: "hero",
// case 1: This should work, I think?
TryToCatchError { title: "This explodes" }
// case 2: This "works", but catches the error at the wrong (outer) boundary
ErrorBoundary {
handle_error: |_| {
// We would NOT expect to see this (but we do)
rsx! { "FAILED" }
},
TryToCatchError { title: "This fails" }
}
}
}
}
Expected behavior
In both cases, I would expect TryToCatchError to catch the error internally, but this never happens.
In the first case, the error bubbles all the way out, replacing the view of our app with a full-screen error message (and no backtrace, for some reason?)
In the second case, the outer ErrorBoundary does seem to catch the error, but never the inner one.
Environment:
- Dioxus version:
dioxus = { version = "0.6.0", features = [] } - Rust version:
rustc 1.85.0 (4d91de4e4 2025-02-17) - OS info:
Debian Stable (12.10) - App platform:
web
It looks like this is a more general issue than just the error boundary component. Context is not provided to event handlers in inline children correctly in any component. This also fails:
// dioxus = { version = "0.6.3", features = ["desktop"] }
// tracing = "0.1.41"
use dioxus::prelude::*;
fn main() {
dioxus::launch(|| {
rsx! {
ProvidesU32 {
button {
onclick: move |_| {
consume_context::<u32>();
},
"this panics"
}
}
ProvidesU32 {
DoesntPanic {}
}
}
});
}
#[component]
fn ProvidesU32(children: Element) -> Element {
use_context_provider(|| 0u32);
children
}
fn DoesntPanic() -> Element {
rsx! {
button {
onclick: move |_| {
consume_context::<u32>();
},
"this doesn't panic"
}
}
}
Ah okay, that explains it.
I'm happy to uncover something worse than I thought, I guess? 😅
But seriously, finding this is a good step, I'm glad we were able to narrow it down, together.