react-rs-core 0.3.0

Core reactive primitives for react.rs - Signal, Effect, Memo, Context
Documentation
use std::marker::PhantomData;

pub trait IntoView {
    type View;
    fn into_view(self) -> Self::View;
}

impl<T> IntoView for T
where
    T: Sized,
{
    type View = T;
    fn into_view(self) -> Self::View {
        self
    }
}

pub struct Component<P, F, V>
where
    F: Fn(P) -> V,
    V: IntoView,
{
    render: F,
    _phantom: PhantomData<P>,
}

impl<P, F, V> Component<P, F, V>
where
    F: Fn(P) -> V,
    V: IntoView,
{
    pub fn new(render: F) -> Self {
        Self {
            render,
            _phantom: PhantomData,
        }
    }

    pub fn call(&self, props: P) -> V::View {
        (self.render)(props).into_view()
    }
}

pub fn component<P, F, V>(render: F) -> Component<P, F, V>
where
    F: Fn(P) -> V,
    V: IntoView,
{
    Component::new(render)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_function_component() {
        fn greeting(name: String) -> String {
            format!("Hello, {}!", name)
        }

        let comp = component(greeting);
        let result = comp.call("World".to_string());
        assert_eq!(result, "Hello, World!");
    }

    #[test]
    fn test_component_with_props_struct() {
        #[derive(Clone)]
        struct ButtonProps {
            label: String,
            disabled: bool,
        }

        fn button_view(props: ButtonProps) -> String {
            if props.disabled {
                format!("[{}] (disabled)", props.label)
            } else {
                format!("[{}]", props.label)
            }
        }

        let button = component(button_view);

        let result = button.call(ButtonProps {
            label: "Click me".to_string(),
            disabled: false,
        });
        assert_eq!(result, "[Click me]");

        let result_disabled = button.call(ButtonProps {
            label: "Submit".to_string(),
            disabled: true,
        });
        assert_eq!(result_disabled, "[Submit] (disabled)");
    }

    #[test]
    fn test_closure_component() {
        let prefix = "Greeting: ";
        let greet = component(move |name: String| format!("{}{}", prefix, name));

        assert_eq!(greet.call("Alice".to_string()), "Greeting: Alice");
    }
}