This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew/
renderer.rs

1use std::cell::Cell;
2use std::panic::PanicHookInfo as PanicInfo;
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/// Set a custom panic hook.
15/// Unless a panic hook is set through this function, Yew will
16/// overwrite any existing panic hook when an application is rendered with [Renderer].
17#[cfg(feature = "csr")]
18#[allow(clippy::incompatible_msrv)]
19pub fn set_custom_panic_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + Sync + Send + 'static>) {
20    std::panic::set_hook(hook);
21    PANIC_HOOK_IS_SET.with(|hook_is_set| hook_is_set.set(true));
22}
23
24fn set_default_panic_hook() {
25    if std::thread::panicking() {
26        // very unlikely, but avoid hitting this when running parallel tests.
27        return;
28    }
29    if !PANIC_HOOK_IS_SET.with(|hook_is_set| hook_is_set.replace(true)) {
30        std::panic::set_hook(Box::new(console_error_panic_hook::hook));
31    }
32}
33
34/// The Yew Renderer.
35///
36/// This is the main entry point of a Yew application.
37#[cfg(feature = "csr")]
38#[derive(Debug)]
39#[must_use = "Renderer does nothing unless render() is called."]
40pub struct Renderer<COMP>
41where
42    COMP: BaseComponent + 'static,
43{
44    root: Element,
45    props: COMP::Properties,
46}
47
48impl<COMP> Default for Renderer<COMP>
49where
50    COMP: BaseComponent + 'static,
51    COMP::Properties: Default,
52{
53    fn default() -> Self {
54        Self::with_props(Default::default())
55    }
56}
57
58impl<COMP> Renderer<COMP>
59where
60    COMP: BaseComponent + 'static,
61    COMP::Properties: Default,
62{
63    /// Creates a [Renderer] that renders into the document body with default properties.
64    pub fn new() -> Self {
65        Self::default()
66    }
67
68    /// Creates a [Renderer] that renders into a custom root with default properties.
69    pub fn with_root(root: Element) -> Self {
70        Self::with_root_and_props(root, Default::default())
71    }
72}
73
74impl<COMP> Renderer<COMP>
75where
76    COMP: BaseComponent + 'static,
77{
78    /// Creates a [Renderer] that renders into the document body with custom properties.
79    pub fn with_props(props: COMP::Properties) -> Self {
80        Self::with_root_and_props(
81            gloo::utils::document()
82                .body()
83                .expect("no body node found")
84                .into(),
85            props,
86        )
87    }
88
89    /// Creates a [Renderer] that renders into a custom root with custom properties.
90    pub fn with_root_and_props(root: Element, props: COMP::Properties) -> Self {
91        Self { root, props }
92    }
93
94    /// Renders the application.
95    pub fn render(self) -> AppHandle<COMP> {
96        set_default_panic_hook();
97        AppHandle::<COMP>::mount_with_props(self.root, Rc::new(self.props))
98    }
99}
100
101#[cfg(feature = "hydration")]
102mod feat_hydration {
103    use super::*;
104
105    impl<COMP> Renderer<COMP>
106    where
107        COMP: BaseComponent + 'static,
108    {
109        /// Hydrates the application.
110        pub fn hydrate(self) -> AppHandle<COMP> {
111            set_default_panic_hook();
112            AppHandle::<COMP>::hydrate_with_props(self.root, Rc::new(self.props))
113        }
114    }
115}