Skip to main content
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;
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")]
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        // very unlikely, but avoid hitting this when running parallel tests.
26        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/// The Yew Renderer.
34///
35/// This is the main entry point of a Yew application.
36#[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    /// Creates a [Renderer] that renders into the document body with default properties.
61    pub fn new() -> Self {
62        Self::default()
63    }
64
65    /// Creates a [Renderer] that renders into a custom root with default properties.
66    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    /// Creates a [Renderer] that renders into the document body with custom properties.
76    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    /// Creates a [Renderer] that renders into a custom root with custom properties.
87    pub fn with_root_and_props(root: Element, props: COMP::Properties) -> Self {
88        Self { root, props }
89    }
90
91    /// Renders the application.
92    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        /// Hydrates the application.
107        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}