react-rs-core 0.3.0

Core reactive primitives for react.rs - Signal, Effect, Memo, Context
Documentation
use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::collections::HashMap;

thread_local! {
    static CONTEXT_MAP: RefCell<HashMap<TypeId, Box<dyn Any>>> = RefCell::new(HashMap::new());
}

pub fn provide_context<T: Clone + 'static>(value: T) {
    CONTEXT_MAP.with(|map| {
        map.borrow_mut().insert(TypeId::of::<T>(), Box::new(value));
    });
}

pub fn use_context<T: Clone + 'static>() -> Option<T> {
    CONTEXT_MAP.with(|map| {
        map.borrow()
            .get(&TypeId::of::<T>())
            .and_then(|v| v.downcast_ref::<T>())
            .cloned()
    })
}

pub fn use_context_or<T: Clone + 'static>(default: T) -> T {
    use_context::<T>().unwrap_or(default)
}

pub fn clear_context<T: 'static>() {
    CONTEXT_MAP.with(|map| {
        map.borrow_mut().remove(&TypeId::of::<T>());
    });
}

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

    #[derive(Clone, Debug, PartialEq)]
    struct Theme {
        dark_mode: bool,
        primary_color: String,
    }

    #[derive(Clone, Debug, PartialEq)]
    struct UserInfo {
        name: String,
        id: u32,
    }

    fn cleanup() {
        clear_context::<Theme>();
        clear_context::<UserInfo>();
        clear_context::<i32>();
    }

    #[test]
    fn test_provide_and_use_context() {
        cleanup();

        let theme = Theme {
            dark_mode: true,
            primary_color: "blue".to_string(),
        };

        provide_context(theme.clone());

        let retrieved = use_context::<Theme>();
        assert_eq!(retrieved, Some(theme));

        cleanup();
    }

    #[test]
    fn test_context_not_found() {
        cleanup();

        let result = use_context::<UserInfo>();
        assert_eq!(result, None);

        cleanup();
    }

    #[test]
    fn test_multiple_contexts() {
        cleanup();

        let theme = Theme {
            dark_mode: false,
            primary_color: "green".to_string(),
        };
        let user = UserInfo {
            name: "Alice".to_string(),
            id: 42,
        };

        provide_context(theme.clone());
        provide_context(user.clone());

        assert_eq!(use_context::<Theme>(), Some(theme));
        assert_eq!(use_context::<UserInfo>(), Some(user));

        cleanup();
    }

    #[test]
    fn test_use_context_or_default() {
        cleanup();

        let default_value = 100;
        let result = use_context_or::<i32>(default_value);
        assert_eq!(result, 100);

        provide_context(42i32);
        let result = use_context_or::<i32>(default_value);
        assert_eq!(result, 42);

        cleanup();
    }

    #[test]
    fn test_context_override() {
        cleanup();

        provide_context(10i32);
        assert_eq!(use_context::<i32>(), Some(10));

        provide_context(20i32);
        assert_eq!(use_context::<i32>(), Some(20));

        cleanup();
    }
}