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

yew/
app_handle.rs

1//! [AppHandle] contains the state Yew keeps to bootstrap a component in an isolated scope.
2
3use std::ops::Deref;
4use std::rc::Rc;
5
6use web_sys::Element;
7
8use crate::dom_bundle::{BSubtree, DomSlot, DynamicDomSlot};
9use crate::html::{BaseComponent, Scope, Scoped};
10
11/// An instance of an application.
12#[derive(Debug)]
13pub struct AppHandle<COMP: BaseComponent> {
14    /// `Scope` holder
15    pub(crate) scope: Scope<COMP>,
16}
17
18impl<COMP> AppHandle<COMP>
19where
20    COMP: BaseComponent,
21{
22    /// The main entry point of a Yew program which also allows passing properties. It works
23    /// similarly to the `program` function in Elm. You should provide an initial model, `update`
24    /// function which will update the state of the model and a `view` function which
25    /// will render the model to a virtual DOM tree.
26    #[tracing::instrument(
27        level = tracing::Level::DEBUG,
28        name = "mount",
29        skip(props),
30    )]
31    pub(crate) fn mount_with_props(host: Element, props: Rc<COMP::Properties>) -> Self {
32        clear_element(&host);
33        let app = Self {
34            scope: Scope::new(None),
35        };
36        let hosting_root = BSubtree::create_root(&host);
37        app.scope.mount_in_place(
38            hosting_root,
39            host,
40            DomSlot::at_end(),
41            DynamicDomSlot::new_debug_trapped(),
42            props,
43        );
44
45        app
46    }
47
48    /// Update the properties of the app's root component.
49    ///
50    /// This can be an alternative to sending and handling messages. The existing component will be
51    /// reused and have its properties updates. This will presumably trigger a re-render, refer to
52    /// the [`changed`] lifecycle for details.
53    ///
54    /// [`changed`]: crate::Component::changed
55    #[tracing::instrument(
56        level = tracing::Level::DEBUG,
57        skip_all,
58    )]
59    pub fn update(&mut self, new_props: COMP::Properties) {
60        self.scope.reuse(Rc::new(new_props), DomSlot::at_end())
61    }
62
63    /// Schedule the app for destruction
64    #[tracing::instrument(
65        level = tracing::Level::DEBUG,
66        skip_all,
67    )]
68    pub fn destroy(self) {
69        self.scope.destroy(false)
70    }
71}
72
73impl<COMP> Deref for AppHandle<COMP>
74where
75    COMP: BaseComponent,
76{
77    type Target = Scope<COMP>;
78
79    fn deref(&self) -> &Self::Target {
80        &self.scope
81    }
82}
83
84/// Removes anything from the given element.
85fn clear_element(host: &Element) {
86    while let Some(child) = host.last_child() {
87        host.remove_child(&child).expect("can't remove a child");
88    }
89}
90
91#[cfg(feature = "hydration")]
92mod feat_hydration {
93    use super::*;
94    use crate::dom_bundle::Fragment;
95
96    impl<COMP> AppHandle<COMP>
97    where
98        COMP: BaseComponent,
99    {
100        #[tracing::instrument(
101            level = tracing::Level::DEBUG,
102            name = "hydrate",
103            skip(props),
104        )]
105        pub(crate) fn hydrate_with_props(host: Element, props: Rc<COMP::Properties>) -> Self {
106            let app = Self {
107                scope: Scope::new(None),
108            };
109
110            let mut fragment = Fragment::collect_children(&host);
111            let hosting_root = BSubtree::create_root(&host);
112
113            app.scope.hydrate_in_place(
114                hosting_root,
115                host.clone(),
116                &mut fragment,
117                DynamicDomSlot::new_debug_trapped(),
118                Rc::clone(&props),
119            );
120            #[cfg(debug_assertions)] // Fix trapped next_sibling at the root
121            app.scope.reuse(props, DomSlot::at_end());
122
123            // We remove all remaining nodes, this mimics the clear_element behaviour in
124            // mount_with_props.
125            for node in fragment.iter() {
126                host.remove_child(node).unwrap();
127            }
128
129            app
130        }
131    }
132}