This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! [AppHandle] contains the state Yew keeps to bootstrap a component in an isolated scope.

use std::ops::Deref;
use std::rc::Rc;

use web_sys::Element;

use crate::dom_bundle::{BSubtree, DomSlot, DynamicDomSlot};
use crate::html::{BaseComponent, Scope, Scoped};

/// An instance of an application.
#[derive(Debug)]
pub struct AppHandle<COMP: BaseComponent> {
    /// `Scope` holder
    pub(crate) scope: Scope<COMP>,
}

impl<COMP> AppHandle<COMP>
where
    COMP: BaseComponent,
{
    /// The main entry point of a Yew program which also allows passing properties. It works
    /// similarly to the `program` function in Elm. You should provide an initial model, `update`
    /// function which will update the state of the model and a `view` function which
    /// will render the model to a virtual DOM tree.
    #[tracing::instrument(
        level = tracing::Level::DEBUG,
        name = "mount",
        skip(props),
    )]
    pub(crate) fn mount_with_props(host: Element, props: Rc<COMP::Properties>) -> Self {
        clear_element(&host);
        let app = Self {
            scope: Scope::new(None),
        };
        let hosting_root = BSubtree::create_root(&host);
        app.scope.mount_in_place(
            hosting_root,
            host,
            DomSlot::at_end(),
            DynamicDomSlot::new_debug_trapped(),
            props,
        );

        app
    }

    /// Update the properties of the app's root component.
    ///
    /// This can be an alternative to sending and handling messages. The existing component will be
    /// reused and have its properties updates. This will presumably trigger a re-render, refer to
    /// the [`changed`] lifecycle for details.
    ///
    /// [`changed`]: crate::Component::changed
    #[tracing::instrument(
        level = tracing::Level::DEBUG,
        skip_all,
    )]
    pub fn update(&mut self, new_props: COMP::Properties) {
        self.scope.reuse(Rc::new(new_props), DomSlot::at_end())
    }

    /// Schedule the app for destruction
    #[tracing::instrument(
        level = tracing::Level::DEBUG,
        skip_all,
    )]
    pub fn destroy(self) {
        self.scope.destroy(false)
    }
}

impl<COMP> Deref for AppHandle<COMP>
where
    COMP: BaseComponent,
{
    type Target = Scope<COMP>;

    fn deref(&self) -> &Self::Target {
        &self.scope
    }
}

/// Removes anything from the given element.
fn clear_element(host: &Element) {
    while let Some(child) = host.last_child() {
        host.remove_child(&child).expect("can't remove a child");
    }
}

#[cfg(feature = "hydration")]
mod feat_hydration {
    use super::*;
    use crate::dom_bundle::Fragment;

    impl<COMP> AppHandle<COMP>
    where
        COMP: BaseComponent,
    {
        #[tracing::instrument(
            level = tracing::Level::DEBUG,
            name = "hydrate",
            skip(props),
        )]
        pub(crate) fn hydrate_with_props(host: Element, props: Rc<COMP::Properties>) -> Self {
            let app = Self {
                scope: Scope::new(None),
            };

            let mut fragment = Fragment::collect_children(&host);
            let hosting_root = BSubtree::create_root(&host);

            app.scope.hydrate_in_place(
                hosting_root,
                host.clone(),
                &mut fragment,
                DynamicDomSlot::new_debug_trapped(),
                Rc::clone(&props),
            );
            #[cfg(debug_assertions)] // Fix trapped next_sibling at the root
            app.scope.reuse(props, DomSlot::at_end());

            // We remove all remaining nodes, this mimics the clear_element behaviour in
            // mount_with_props.
            for node in fragment.iter() {
                host.remove_child(node).unwrap();
            }

            app
        }
    }
}