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};
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        let _ = app
38            .scope
39            .mount_in_place(hosting_root, host, DomSlot::at_end(), props);
40
41        app
42    }
43
44    /// Update the properties of the app's root component.
45    ///
46    /// This can be an alternative to sending and handling messages. The existing component will be
47    /// reused and have its properties updates. This will presumably trigger a re-render, refer to
48    /// the [`changed`] lifecycle for details.
49    ///
50    /// [`changed`]: crate::Component::changed
51    #[tracing::instrument(
52        level = tracing::Level::DEBUG,
53        skip_all,
54    )]
55    pub fn update(&mut self, new_props: COMP::Properties) {
56        self.scope.reuse(Rc::new(new_props), DomSlot::at_end())
57    }
58
59    /// Schedule the app for destruction
60    #[tracing::instrument(
61        level = tracing::Level::DEBUG,
62        skip_all,
63    )]
64    pub fn destroy(self) {
65        self.scope.destroy(false)
66    }
67}
68
69impl<COMP> Deref for AppHandle<COMP>
70where
71    COMP: BaseComponent,
72{
73    type Target = Scope<COMP>;
74
75    fn deref(&self) -> &Self::Target {
76        &self.scope
77    }
78}
79
80/// Removes anything from the given element.
81fn clear_element(host: &Element) {
82    while let Some(child) = host.last_child() {
83        host.remove_child(&child).expect("can't remove a child");
84    }
85}
86
87#[cfg(feature = "hydration")]
88mod feat_hydration {
89    use super::*;
90    use crate::dom_bundle::Fragment;
91
92    impl<COMP> AppHandle<COMP>
93    where
94        COMP: BaseComponent,
95    {
96        #[tracing::instrument(
97            level = tracing::Level::DEBUG,
98            name = "hydrate",
99            skip(props),
100        )]
101        pub(crate) fn hydrate_with_props(host: Element, props: Rc<COMP::Properties>) -> Self {
102            let app = Self {
103                scope: Scope::new(None),
104            };
105
106            let mut fragment = Fragment::collect_children(&host);
107            let hosting_root = BSubtree::create_root(&host);
108
109            let mut previous_next_sibling = None;
110            app.scope.hydrate_in_place(
111                hosting_root,
112                host.clone(),
113                &mut fragment,
114                Rc::clone(&props),
115                &mut previous_next_sibling,
116            );
117            if let Some(previous_next_sibling) = previous_next_sibling {
118                previous_next_sibling.reassign(DomSlot::at_end());
119            }
120
121            // We remove all remaining nodes, this mimics the clear_element behaviour in
122            // mount_with_props.
123            for node in fragment.iter() {
124                host.remove_child(node).unwrap();
125            }
126
127            app
128        }
129    }
130}