Skip to content

Allowing ErrorBoundary to work smoothly with Error #4157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
chunleng opened this issue May 20, 2025 · 3 comments
Open

Allowing ErrorBoundary to work smoothly with Error #4157

chunleng opened this issue May 20, 2025 · 3 comments
Labels
enhancement New feature or request

Comments

@chunleng
Copy link

Feature Request

I gave ErrorBoundary a try according to the guide. However, when I try use it with some errors, I realize that it might be a little hard to get it to play nicely with the errors. So for example:

use dioxus::prelude::*;
use thiserror::Error;

fn main() {
    dioxus::launch(|| {
        rsx! {
            ErrorBoundary {
                handle_error: |e: ErrorContext| { rsx! {
                    {e.show()}
                } },
                Foo {}
            }
        }
    });
}

#[component]
fn Foo() -> Element {
    Err(AppError::SomeError).show(|e| rsx! {{e.to_string()}})?;
    rsx! {}
}

#[derive(Error, Debug)]
enum AppError {
    #[error("Some error has occurred")]
    SomeError,
}

In order to show the error message in the error, it seems that I have to add .show(|e| rsx! {{e.to_string()}}) to every Result carrying the error. This is a little of a hassle and I thought there should be a better way.

Implement Suggestion

Method 1

Maybe, make CaptureError capture information about the original error. (Maybe we can downcast? Didn't seem to work in my case and rather than downcast, it's better to work with the actual error)

            ErrorBoundary {
                handle_error: |e: ErrorContext| { rsx! {
                    for x in e.errors().iter().map(|e| { e.error.to_string() }){
                        {x}
                    }
                } },
                Foo {}
            }

Method 2

Allow a way for user to implement a trait that can override Into trait. For example:

impl Into<CapturedError> for AppError {
    fn into(self) -> CapturedError {
        Err::<(), _>(self)
            .show(|x| {
                rsx! {
                    {x.to_string()}
                }
            })
            .unwrap_err()
    }
}
@chunleng chunleng added the enhancement New feature or request label May 20, 2025
@chunleng
Copy link
Author

chunleng commented May 20, 2025

I am more in favor of method 2 as it provide more flexibility, in case of Apps with more well-managed error, we can create an Into trait implementation that automatically use App level error, such as:

#[derive(Error, Debug)]
enum AppError {
    #[error("Error 1-{0}")]
    SomeError(#[from] DisplayError),
}

#[derive(Error, Debug)]
enum DisplayError {
    #[error("1")]
    Unknown
}

impl Into<CapturedError> for DisplayError {
    fn into(self) -> CapturedError {
        Err::<(), _>(self)
            .show(|x| {
                rsx! {
                    {AppError::from(x).to_string()} // <- Make DisplayError into AppError before returning the display format
                }
            })
            .unwrap_err()
    }
}

@ealmloff
Copy link
Member

Captured error has a default from implementation for any type that implements Error. You can display the error by formatting it like this:

use dioxus::prelude::*;
use thiserror::Error;

fn main() {
    dioxus::launch(|| {
        rsx! {
            ErrorBoundary {
                handle_error: |e: ErrorContext| { rsx! {
                    for error in e.errors() {
                        p { "{error}" }
                    }
                } },
                Foo {}
            }
        }
    });
}

#[component]
fn Foo() -> Element {
    Err(AppError::SomeError)?;
    rsx! {}
}

#[derive(Error, Debug)]
enum AppError {
    #[error("Some error has occurred")]
    SomeError,
}

You can also downcast errors like this:

use dioxus::prelude::*;
use thiserror::Error;

fn main() {
    dioxus::launch(|| {
        rsx! {
            ErrorBoundary {
                handle_error: |e: ErrorContext| { rsx! {
                    for e in e.errors() {
                        {
                            if let Some(my_error) = e.downcast::<AppError>() {
                                rsx! { div { "App Error {my_error}" } }
                            } else {
                                rsx! { div { "{e}" } }
                            }
                        }
                    }
                } },
                Foo {}
            }
        }
    });
}

#[component]
fn Foo() -> Element {
    Err(AppError::SomeError)?;
    rsx! {}
}

#[derive(Error, Debug)]
enum AppError {
    #[error("Some error has occurred")]
    SomeError,
}

We could expose a method to get the inner &dyn Error from the Captured error to give more flexibility in how it is displayed

@chunleng
Copy link
Author

Thank you for the quick reply!

Captured error has a default from implementation for any type that implements Error. You can display the error by formatting it like this:

I understand the basic implementation, it's just hard to make it "play nicely" with the original errors because everything ends up as CapturedError which limits access to the original error and therefore also limits the formatting of error.

if let Some(my_error) = e.downcast::<AppError>() {
                                rsx! { div { "App Error {my_error}" } }

Downcasting does not seem to get into this branch on this code

We could expose a method to get the inner &dyn Error from the Captured error to give more flexibility in how it is displayed

I guess it will help a great deal if an owned error can be passed as it will help with the handling in the structure Error case mention here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants