1use std::cell::Cell;
2#[rustversion::since(1.81)]
3use std::panic::PanicHookInfo as PanicInfo;
4#[rustversion::before(1.81)]
5use std::panic::PanicInfo;
6use std::rc::Rc;
7
8use web_sys::Element;
9
10use crate::app_handle::AppHandle;
11use crate::html::BaseComponent;
12
13thread_local! {
14 static PANIC_HOOK_IS_SET: Cell<bool> = const { Cell::new(false) };
15}
16
17#[cfg(feature = "csr")]
21#[allow(clippy::incompatible_msrv)]
22pub fn set_custom_panic_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + Sync + Send + 'static>) {
23 std::panic::set_hook(hook);
24 PANIC_HOOK_IS_SET.with(|hook_is_set| hook_is_set.set(true));
25}
26
27fn set_default_panic_hook() {
28 if std::thread::panicking() {
29 return;
31 }
32 if !PANIC_HOOK_IS_SET.with(|hook_is_set| hook_is_set.replace(true)) {
33 std::panic::set_hook(Box::new(console_error_panic_hook::hook));
34 }
35}
36
37#[cfg(feature = "csr")]
41#[derive(Debug)]
42#[must_use = "Renderer does nothing unless render() is called."]
43pub struct Renderer<COMP>
44where
45 COMP: BaseComponent + 'static,
46{
47 root: Element,
48 props: COMP::Properties,
49}
50
51impl<COMP> Default for Renderer<COMP>
52where
53 COMP: BaseComponent + 'static,
54 COMP::Properties: Default,
55{
56 fn default() -> Self {
57 Self::with_props(Default::default())
58 }
59}
60
61impl<COMP> Renderer<COMP>
62where
63 COMP: BaseComponent + 'static,
64 COMP::Properties: Default,
65{
66 pub fn new() -> Self {
68 Self::default()
69 }
70
71 pub fn with_root(root: Element) -> Self {
73 Self::with_root_and_props(root, Default::default())
74 }
75}
76
77impl<COMP> Renderer<COMP>
78where
79 COMP: BaseComponent + 'static,
80{
81 pub fn with_props(props: COMP::Properties) -> Self {
83 Self::with_root_and_props(
84 gloo::utils::document()
85 .body()
86 .expect("no body node found")
87 .into(),
88 props,
89 )
90 }
91
92 pub fn with_root_and_props(root: Element, props: COMP::Properties) -> Self {
94 Self { root, props }
95 }
96
97 pub fn render(self) -> AppHandle<COMP> {
99 set_default_panic_hook();
100 AppHandle::<COMP>::mount_with_props(self.root, Rc::new(self.props))
101 }
102}
103
104#[cfg(feature = "hydration")]
105mod feat_hydration {
106 use super::*;
107
108 impl<COMP> Renderer<COMP>
109 where
110 COMP: BaseComponent + 'static,
111 {
112 pub fn hydrate(self) -> AppHandle<COMP> {
114 set_default_panic_hook();
115 AppHandle::<COMP>::hydrate_with_props(self.root, Rc::new(self.props))
116 }
117 }
118}