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