feat(panic_hook): implement custom panic hook for enhanced diagnostics on Leptos owner disposal

This commit is contained in:
ryan 2025-05-09 16:31:53 +03:00
parent 24c138b866
commit 46b6cf82e2
3 changed files with 63 additions and 2 deletions

View file

@ -5,6 +5,7 @@ use tokio::sync::Mutex;
use compareware::db::Database;
use compareware::api::{ItemRequest, create_item, get_items, get_selected_properties, add_selected_property};
use compareware::models::item::Item;
use compareware::utils::panic_hook;
#[cfg(feature = "ssr")]
#[actix_web::main]
@ -19,6 +20,8 @@ async fn main() -> std::io::Result<()> {
use std::sync::Arc;
use tokio::sync::Mutex;
panic_hook::init();
// Setup logging
std::env::set_var("RUST_LOG", "info");
@ -167,12 +170,15 @@ pub fn main() {
#[cfg(all(not(feature = "ssr"), feature = "csr"))]
pub fn main() {
// Initialize custom panic hook for better diagnostics
panic_hook::init();
// a client-side main function is required for using `trunk serve`
// prefer using `cargo leptos serve` instead
// to run: `trunk serve --open --features csr`
use compareware::app::*;
console_error_panic_hook::set_once();
// console_error_panic_hook::set_once();
leptos::mount_to_body(App);
}

View file

@ -1 +1,2 @@
pub mod leptos_owner;
pub mod panic_hook;

54
src/utils/panic_hook.rs Normal file
View file

@ -0,0 +1,54 @@
use std::panic;
use leptos::logging::log;
use wasm_bindgen::prelude::*;
/// Sets up a custom panic hook that provides more context for Leptos owner disposal panics
pub fn set_custom_panic_hook() {
let original_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
// Call the original hook first
original_hook(panic_info);
// Extract panic message
let message = if let Some(s) = panic_info.payload().downcast_ref::<String>() {
s.clone()
} else if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
s.to_string()
} else {
"Unknown panic".to_string()
};
// Check if this is an owner disposal panic
if message.contains("OwnerDisposed") {
log!("[PANIC] Leptos owner disposal detected. This usually happens when:");
log!("[PANIC] 1. A component has been unmounted but JavaScript is still calling into Rust");
log!("[PANIC] 2. An effect or signal update is running after the component is gone");
log!("[PANIC] 3. A closure or callback is being called after cleanup");
// Log current component registry state
let js_code = r#"
if (window.typeaheadRegistry) {
console.log('[PANIC] Current typeahead registry:',
Object.keys(window.typeaheadRegistry).map(id => ({
id,
alive: window.typeaheadRegistry[id].alive,
initialized: window.typeaheadRegistry[id].initialized
}))
);
} else {
console.log('[PANIC] No typeahead registry found');
}
"#;
let _ = js_sys::eval(js_code);
}
}));
}
/// Call in main.rs or app initialization
pub fn init() {
log!("[PANIC_HOOK] Setting up custom panic hook");
set_custom_panic_hook();
log!("[PANIC_HOOK] Custom panic hook set up successfully");
}