Repository: leptos-rs/leptos
File: reactive_graph/src/computed/selector.rs (Rust)

Code before:
/// Effect::new({
///     let is_selected = is_selected.clone();
///     move |_| {
///         if is_selected.selected(5) {
///             total_notifications.update_value(|n| *n += 1);
///         }
///     }
/// });
///
/// assert_eq!(is_selected.selected(5), false);
/// assert_eq!(total_notifications.get_value(), 0);
/// a.set(5);
/// # any_spawner::Executor::tick().await;
///
/// assert_eq!(is_selected.selected(5), true);
/// assert_eq!(total_notifications.get_value(), 1);
/// a.set(5);
/// # any_spawner::Executor::tick().await;
///
/// assert_eq!(is_selected.selected(5), true);
/// assert_eq!(total_notifications.get_value(), 1);
/// a.set(4);
///
/// # any_spawner::Executor::tick().await;
/// assert_eq!(is_selected.selected(5), false);
/// # // TODO: this test is really failing, put `# }).await;` in the next line
/// # });
/// # });
/// ```
#[derive(Clone)]
pub struct Selector<T>
where
    T: PartialEq + Eq + Clone + Hash + 'static,
{
    subs: Arc<RwLock<FxHashMap<T, ArcRwSignal<bool>>>>,
    v: Arc<RwLock<Option<T>>>,
    #[allow(clippy::type_complexity)]
    f: Arc<dyn Fn(&T, &T) -> bool + Send + Sync>,
    // owning the effect keeps it alive, to keep updating the selector
    #[allow(dead_code)]
    effect: Arc<RenderEffect<T>>,
}

impl<T> Selector<T>
where
    T: PartialEq + Eq + Clone + Hash + 'static,
{
    /// Creates a new selector that compares values using [`PartialEq`].
    pub fn new(source: impl Fn() -> T + Clone + 'static) -> Self {
        Self::new_with_fn(source, PartialEq::eq)
    }

Review comment (suggestion):
The call to `is_selected.v.read()` returns `Ok(None)`, so it panics with "called `Option::unwrap()` on a `None` value" at `selected()` method.

Code after:
/// Effect::new_isomorphic({
///     let is_selected = is_selected.clone();
///     move |_| {
///         if is_selected.selected(5) {
///             total_notifications.update_value(|n| *n += 1);
///         }
///     }
/// });
///
/// assert_eq!(is_selected.selected(5), false);
/// assert_eq!(total_notifications.get_value(), 0);
/// a.set(5);
/// # any_spawner::Executor::tick().await;
///
/// assert_eq!(is_selected.selected(5), true);
/// assert_eq!(total_notifications.get_value(), 1);
/// a.set(5);
/// # any_spawner::Executor::tick().await;
///
/// assert_eq!(is_selected.selected(5), true);
/// assert_eq!(total_notifications.get_value(), 1);
/// a.set(4);
///
/// # any_spawner::Executor::tick().await;
/// assert_eq!(is_selected.selected(5), false);
/// # }).await;
/// # });
/// ```
#[derive(Clone)]
pub struct Selector<T>
where
    T: PartialEq + Eq + Clone + Hash + 'static,
{
    subs: Arc<RwLock<FxHashMap<T, ArcRwSignal<bool>>>>,
    v: Arc<RwLock<Option<T>>>,
    #[allow(clippy::type_complexity)]
    f: Arc<dyn Fn(&T, &T) -> bool + Send + Sync>,
    // owning the effect keeps it alive, to keep updating the selector
    #[allow(dead_code)]
    effect: Arc<RenderEffect<T>>,
}

impl<T> Selector<T>
where
    T: PartialEq + Send + Sync + Eq + Clone + Hash + 'static,
{
    /// Creates a new selector that compares values using [`PartialEq`].
    pub fn new(source: impl Fn() -> T + Send + Sync + Clone + 'static) -> Self {
        Self::new_with_fn(source, PartialEq::eq)
    }