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")]
21pub fn set_custom_panic_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + Sync + Send + 'static>) {
22 std::panic::set_hook(hook);
23 PANIC_HOOK_IS_SET.with(|hook_is_set| hook_is_set.set(true));
24}
25
26fn set_default_panic_hook() {
27 if std::thread::panicking() {
28 return;
30 }
31 if !PANIC_HOOK_IS_SET.with(|hook_is_set| hook_is_set.replace(true)) {
32 std::panic::set_hook(Box::new(console_error_panic_hook::hook));
33 }
34}
35
36#[cfg(feature = "csr")]
40#[derive(Debug)]
41#[must_use = "Renderer does nothing unless render() is called."]
42pub struct Renderer<COMP>
43where
44 COMP: BaseComponent + 'static,
45{
46 root: Element,
47 props: COMP::Properties,
48}
49
50impl<COMP> Default for Renderer<COMP>
51where
52 COMP: BaseComponent + 'static,
53 COMP::Properties: Default,
54{
55 fn default() -> Self {
56 Self::with_props(Default::default())
57 }
58}
59
60impl<COMP> Renderer<COMP>
61where
62 COMP: BaseComponent + 'static,
63 COMP::Properties: Default,
64{
65 pub fn new() -> Self {
67 Self::default()
68 }
69
70 pub fn with_root(root: Element) -> Self {
72 Self::with_root_and_props(root, Default::default())
73 }
74}
75
76impl<COMP> Renderer<COMP>
77where
78 COMP: BaseComponent + 'static,
79{
80 pub fn with_props(props: COMP::Properties) -> Self {
82 Self::with_root_and_props(
83 gloo::utils::document()
84 .body()
85 .expect("no body node found")
86 .into(),
87 props,
88 )
89 }
90
91 pub fn with_root_and_props(root: Element, props: COMP::Properties) -> Self {
93 Self { root, props }
94 }
95
96 pub fn render(self) -> AppHandle<COMP> {
98 set_default_panic_hook();
99 AppHandle::<COMP>::mount_with_props(self.root, Rc::new(self.props))
100 }
101}
102
103#[cfg(feature = "hydration")]
104mod feat_hydration {
105 use super::*;
106
107 impl<COMP> Renderer<COMP>
108 where
109 COMP: BaseComponent + 'static,
110 {
111 pub fn hydrate(self) -> AppHandle<COMP> {
113 set_default_panic_hook();
114 AppHandle::<COMP>::hydrate_with_props(self.root, Rc::new(self.props))
115 }
116 }
117}