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