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}