dioxus icon indicating copy to clipboard operation
dioxus copied to clipboard

Context is not provided to event handlers in inline component children

Open chrivers opened this issue 8 months ago • 2 comments

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

chrivers avatar Apr 19 '25 11:04 chrivers

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"
        }
    }
}

ealmloff avatar Apr 21 '25 19:04 ealmloff

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.

chrivers avatar Apr 22 '25 13:04 chrivers