mod children;
#[cfg(any(feature = "csr", feature = "ssr"))]
mod lifecycle;
mod marker;
mod properties;
mod scope;
use std::rc::Rc;
pub use children::*;
pub use marker::*;
pub use properties::*;
#[cfg(feature = "csr")]
pub(crate) use scope::Scoped;
pub use scope::{AnyScope, Scope, SendAsMessage};
use super::{Html, HtmlResult, IntoHtmlResult};
#[cfg(feature = "hydration")]
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum RenderMode {
Hydration,
Render,
#[cfg(feature = "ssr")]
Ssr,
}
#[derive(Debug)]
pub struct Context<COMP: BaseComponent> {
scope: Scope<COMP>,
props: Rc<COMP::Properties>,
#[cfg(feature = "hydration")]
creation_mode: RenderMode,
#[cfg(feature = "hydration")]
prepared_state: Option<String>,
}
impl<COMP: BaseComponent> Context<COMP> {
#[inline]
pub fn link(&self) -> &Scope<COMP> {
&self.scope
}
#[inline]
pub fn props(&self) -> &COMP::Properties {
&self.props
}
#[cfg(feature = "hydration")]
pub(crate) fn creation_mode(&self) -> RenderMode {
self.creation_mode
}
pub fn prepared_state(&self) -> Option<&str> {
#[cfg(not(feature = "hydration"))]
let state = None;
#[cfg(feature = "hydration")]
let state = self.prepared_state.as_deref();
state
}
}
pub trait BaseComponent: Sized + 'static {
type Message: 'static;
type Properties: Properties;
fn create(ctx: &Context<Self>) -> Self;
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool;
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool;
fn view(&self, ctx: &Context<Self>) -> HtmlResult;
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool);
fn destroy(&mut self, ctx: &Context<Self>);
fn prepare_state(&self) -> Option<String>;
}
pub trait Component: Sized + 'static {
type Message: 'static;
type Properties: Properties;
fn create(ctx: &Context<Self>) -> Self;
#[allow(unused_variables)]
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
true
}
#[allow(unused_variables)]
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
true
}
fn view(&self, ctx: &Context<Self>) -> Html;
#[allow(unused_variables)]
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {}
fn prepare_state(&self) -> Option<String> {
None
}
#[allow(unused_variables)]
fn destroy(&mut self, ctx: &Context<Self>) {}
}
impl<T> BaseComponent for T
where
T: Sized + Component + 'static,
{
type Message = <T as Component>::Message;
type Properties = <T as Component>::Properties;
fn create(ctx: &Context<Self>) -> Self {
Component::create(ctx)
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
Component::update(self, ctx, msg)
}
fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool {
Component::changed(self, ctx, old_props)
}
fn view(&self, ctx: &Context<Self>) -> HtmlResult {
Component::view(self, ctx).into_html_result()
}
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
Component::rendered(self, ctx, first_render)
}
fn destroy(&mut self, ctx: &Context<Self>) {
Component::destroy(self, ctx)
}
fn prepare_state(&self) -> Option<String> {
Component::prepare_state(self)
}
}
#[cfg(test)]
#[cfg(any(feature = "ssr", feature = "csr"))]
mod tests {
use super::*;
struct MyCustomComponent;
impl Component for MyCustomComponent {
type Message = ();
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self
}
fn view(&self, _ctx: &Context<Self>) -> Html {
Default::default()
}
}
#[test]
fn make_sure_component_update_and_changed_rerender() {
let mut comp = MyCustomComponent;
let ctx = Context {
scope: Scope::new(None),
props: Rc::new(()),
#[cfg(feature = "hydration")]
creation_mode: crate::html::RenderMode::Hydration,
#[cfg(feature = "hydration")]
prepared_state: None,
};
assert!(Component::update(&mut comp, &ctx, ()));
assert!(Component::changed(&mut comp, &ctx, &Rc::new(())));
}
}