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/html/component/
mod.rs

1//! Components wrapped with context including properties, state, and link
2
3mod children;
4#[cfg(any(feature = "csr", feature = "ssr"))]
5mod lifecycle;
6mod marker;
7mod properties;
8mod scope;
9
10use std::rc::Rc;
11
12pub use children::*;
13pub use marker::*;
14pub use properties::*;
15#[cfg(feature = "csr")]
16pub(crate) use scope::Scoped;
17pub use scope::{AnyScope, Scope, SendAsMessage};
18
19use super::{Html, HtmlResult, IntoHtmlResult};
20
21#[cfg(feature = "hydration")]
22#[derive(Debug, Clone, Copy, PartialEq)]
23pub(crate) enum RenderMode {
24    Hydration,
25    Render,
26    #[cfg(feature = "ssr")]
27    Ssr,
28}
29
30/// The [`Component`]'s context. This contains component's [`Scope`] and props and
31/// is passed to every lifecycle method.
32#[derive(Debug)]
33pub struct Context<COMP: BaseComponent> {
34    scope: Scope<COMP>,
35    props: Rc<COMP::Properties>,
36    #[cfg(feature = "hydration")]
37    creation_mode: RenderMode,
38
39    #[cfg(feature = "hydration")]
40    prepared_state: Option<String>,
41}
42
43impl<COMP: BaseComponent> Context<COMP> {
44    /// The component scope
45    #[inline]
46    pub fn link(&self) -> &Scope<COMP> {
47        &self.scope
48    }
49
50    /// The component's props
51    #[inline]
52    pub fn props(&self) -> &COMP::Properties {
53        &self.props
54    }
55
56    /// The component's props as an Rc
57    #[inline]
58    pub(crate) fn rc_props(&self) -> &Rc<COMP::Properties> {
59        &self.props
60    }
61
62    #[cfg(feature = "hydration")]
63    pub(crate) fn creation_mode(&self) -> RenderMode {
64        self.creation_mode
65    }
66
67    /// The component's prepared state
68    pub fn prepared_state(&self) -> Option<&str> {
69        #[cfg(not(feature = "hydration"))]
70        let state = None;
71
72        #[cfg(feature = "hydration")]
73        let state = self.prepared_state.as_deref();
74
75        state
76    }
77}
78
79/// The common base of both function components and struct components.
80///
81/// If you are taken here by doc links, you might be looking for [`Component`] or
82/// [`#[component]`](crate::functional::component).
83///
84/// We provide a blanket implementation of this trait for every member that implements
85/// [`Component`].
86///
87/// # Warning
88///
89/// This trait may be subject to heavy changes between versions and is not intended for direct
90/// implementation.
91///
92/// You should used the [`Component`] trait or the
93/// [`#[component]`](crate::functional::component) macro to define your
94/// components.
95pub trait BaseComponent: Sized + 'static {
96    /// The Component's Message.
97    type Message: 'static;
98
99    /// The Component's Properties.
100    type Properties: Properties;
101
102    /// Creates a component.
103    fn create(ctx: &Context<Self>) -> Self;
104
105    /// Updates component's internal state.
106    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool;
107
108    /// React to changes of component properties.
109    fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool;
110
111    /// Returns a component layout to be rendered.
112    fn view(&self, ctx: &Context<Self>) -> HtmlResult;
113
114    /// Notified after a layout is rendered.
115    fn rendered(&mut self, ctx: &Context<Self>, first_render: bool);
116
117    /// Notified before a component is destroyed.
118    fn destroy(&mut self, ctx: &Context<Self>);
119
120    /// Prepares the server-side state.
121    fn prepare_state(&self) -> Option<String>;
122}
123
124/// Components are the basic building blocks of the UI in a Yew app. Each Component
125/// chooses how to display itself using received props and self-managed state.
126/// Components can be dynamic and interactive by declaring messages that are
127/// triggered and handled asynchronously. This async update mechanism is inspired by
128/// Elm and the actor model used in the Actix framework.
129pub trait Component: Sized + 'static {
130    /// Messages are used to make Components dynamic and interactive. Simple
131    /// Component's can declare their Message type to be `()`. Complex Component's
132    /// commonly use an enum to declare multiple Message types.
133    type Message: 'static;
134
135    /// The Component's properties.
136    ///
137    /// When the parent of a Component is re-rendered, it will either be re-created or
138    /// receive new properties in the context passed to the `changed` lifecycle method.
139    type Properties: Properties;
140
141    /// Called when component is created.
142    fn create(ctx: &Context<Self>) -> Self;
143
144    /// Called when a new message is sent to the component via its scope.
145    ///
146    /// Components handle messages in their `update` method and commonly use this method
147    /// to update their state and (optionally) re-render themselves.
148    ///
149    /// Returned bool indicates whether to render this Component after update.
150    ///
151    /// By default, this function will return true and thus make the component re-render.
152    #[allow(unused_variables)]
153    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
154        true
155    }
156
157    /// Called when properties passed to the component change
158    ///
159    /// Returned bool indicates whether to render this Component after changed.
160    ///
161    /// By default, this function will return true and thus make the component re-render.
162    #[allow(unused_variables)]
163    fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
164        true
165    }
166
167    /// Components define their visual layout using a JSX-style syntax through the use of the
168    /// `html!` procedural macro. The full guide to using the macro can be found in [Yew's
169    /// documentation](https://yew.rs/concepts/html).
170    ///
171    /// Note that `view()` calls do not always follow a render request from `update()` or
172    /// `changed()`. Yew may optimize some calls out to reduce virtual DOM tree generation overhead.
173    /// The `create()` call is always followed by a call to `view()`.
174    fn view(&self, ctx: &Context<Self>) -> Html;
175
176    /// The `rendered` method is called after each time a Component is rendered but
177    /// before the browser updates the page.
178    ///
179    /// Note that `rendered()` calls do not always follow a render request from `update()` or
180    /// `changed()`. Yew may optimize some calls out to reduce virtual DOM tree generation overhead.
181    /// The `create()` call is always followed by a call to `view()` and later `rendered()`.
182    #[allow(unused_variables)]
183    fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {}
184
185    /// Prepares the state during server side rendering.
186    ///
187    /// This state will be sent to the client side and is available via `ctx.prepared_state()`.
188    ///
189    /// This method is only called during server-side rendering after the component has been
190    /// rendered.
191    fn prepare_state(&self) -> Option<String> {
192        None
193    }
194
195    /// Called right before a Component is unmounted.
196    #[allow(unused_variables)]
197    fn destroy(&mut self, ctx: &Context<Self>) {}
198}
199
200impl<T> BaseComponent for T
201where
202    T: Sized + Component + 'static,
203{
204    type Message = <T as Component>::Message;
205    type Properties = <T as Component>::Properties;
206
207    fn create(ctx: &Context<Self>) -> Self {
208        Component::create(ctx)
209    }
210
211    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
212        Component::update(self, ctx, msg)
213    }
214
215    fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool {
216        Component::changed(self, ctx, old_props)
217    }
218
219    fn view(&self, ctx: &Context<Self>) -> HtmlResult {
220        Component::view(self, ctx).into_html_result()
221    }
222
223    fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
224        Component::rendered(self, ctx, first_render)
225    }
226
227    fn destroy(&mut self, ctx: &Context<Self>) {
228        Component::destroy(self, ctx)
229    }
230
231    fn prepare_state(&self) -> Option<String> {
232        Component::prepare_state(self)
233    }
234}
235
236#[cfg(test)]
237#[cfg(any(feature = "ssr", feature = "csr"))]
238mod tests {
239    use super::*;
240
241    struct MyCustomComponent;
242
243    impl Component for MyCustomComponent {
244        type Message = ();
245        type Properties = ();
246
247        fn create(_ctx: &Context<Self>) -> Self {
248            Self
249        }
250
251        fn view(&self, _ctx: &Context<Self>) -> Html {
252            Default::default()
253        }
254    }
255
256    #[test]
257    fn make_sure_component_update_and_changed_rerender() {
258        let mut comp = MyCustomComponent;
259        let ctx = Context {
260            scope: Scope::new(None),
261            props: Rc::new(()),
262            #[cfg(feature = "hydration")]
263            creation_mode: crate::html::RenderMode::Hydration,
264            #[cfg(feature = "hydration")]
265            prepared_state: None,
266        };
267        assert!(Component::update(&mut comp, &ctx, ()));
268        assert!(Component::changed(&mut comp, &ctx, &Rc::new(())));
269    }
270}