reactive_state/
listener.rs

1use std::rc::{Rc, Weak};
2
3/// A trait to take a [Callback] or other custom callback type and
4/// produce a [Listener], a weak reference to that callback.
5pub trait AsListener<State, Event> {
6    /// Produce a [Listener], a weak reference to this callback.
7    fn as_listener(&self) -> Listener<State, Event>;
8}
9
10/// A weak reference to a callback function (usually [Callback]) which
11/// is notified of changes to [Store](crate::Store) `State`, and
12/// `Event`s produced by the store.
13///
14/// See [Callback](Callback) for more information about how this is
15/// typically used.
16#[derive(Clone)]
17pub struct Listener<State, Event>(Weak<dyn Fn(Rc<State>, Option<Event>)>);
18
19impl<State, Event> Listener<State, Event> {
20    /// Attempt to upgrade the weak reference in this listener to a
21    /// [Callback], otherwise if unable to, returns `None`.
22    pub fn as_callback(&self) -> Option<Callback<State, Event>> {
23        match self.0.upgrade() {
24            Some(listener_rc) => Some(Callback(listener_rc)),
25            None => None,
26        }
27    }
28}
29
30impl<State, Event> AsListener<State, Event> for Listener<State, Event> {
31    fn as_listener(&self) -> Listener<State, Event> {
32        Listener(self.0.clone())
33    }
34}
35
36/// A wrapper for a callback which is notified of changes to
37/// [Store](crate::Store) `State`, and `Event`s produced by the store.
38///
39/// ## Example
40///
41/// The following example makes use of the [AsListener](AsListener)
42/// trait implementation for `Callback` which allows it to be used in
43/// [Store::subscribe()](crate::Store::subscribe). The
44/// [AsListener](AsListener) trait creates a weak reference to this
45/// callback in a [Listener](Listener), which is given to the
46/// [Store](crate::Store). When the callback is dropped, the listener will be
47/// removed from the store.
48///
49/// ```
50/// # use reactive_state::{ReducerFn, Store, ReducerResult};
51/// # let reducer: ReducerFn<(), (), (), ()> = |_state, _action| { ReducerResult::default() };
52/// # let store = Store::new(reducer, ());
53/// use reactive_state::Callback;
54///
55/// let callback = Callback::new(|_state, _event| {
56///     println!("Callback invoked");
57/// });
58///
59/// store.subscribe(&callback);
60/// ```
61///
62/// ## Optional Features
63///
64/// If the `"yew"` crate feature is enabled, a number of `From`
65/// implementations are available to convert `yew` callbacks into
66/// this:
67///
68/// + `From<yew::Callback<Rc<State>>>`
69/// + `From<yew::Callback<(Rc<State>, Event)>>`
70/// + `From<yew::Callback<()>>`
71#[derive(Clone)]
72pub struct Callback<State, Event>(Rc<dyn Fn(Rc<State>, Option<Event>)>);
73
74impl<State, Event> AsListener<State, Event> for &Callback<State, Event> {
75    fn as_listener(&self) -> Listener<State, Event> {
76        Listener(Rc::downgrade(&self.0))
77    }
78}
79
80impl<State, Event> Callback<State, Event> {
81    pub fn new<C: Fn(Rc<State>, Option<Event>) + 'static>(closure: C) -> Self {
82        Callback(Rc::new(closure))
83    }
84    pub fn emit(&self, state: Rc<State>, event: Option<Event>) {
85        (self.0)(state, event)
86    }
87}
88
89impl<C, State, Event> From<C> for Callback<State, Event>
90where
91    C: Fn(Rc<State>, Option<Event>) + 'static,
92{
93    fn from(closure: C) -> Self {
94        Callback(Rc::new(closure))
95    }
96}
97
98#[cfg(feature = "yew")]
99#[cfg_attr(docsrs, doc(cfg(feature = "yew")))]
100impl<State, Event> From<yew::Callback<Rc<State>>> for Callback<State, Event>
101where
102    State: 'static,
103    Event: 'static,
104{
105    fn from(yew_callback: yew::Callback<Rc<State>>) -> Self {
106        Callback(Rc::new(move |state, _| {
107            yew_callback.emit(state);
108        }))
109    }
110}
111
112#[cfg(feature = "yew")]
113#[cfg_attr(docsrs, doc(cfg(feature = "yew")))]
114impl<State, Event> From<yew::Callback<(Rc<State>, Option<Event>)>> for Callback<State, Event>
115where
116    State: 'static,
117    Event: 'static,
118{
119    fn from(yew_callback: yew::Callback<(Rc<State>, Option<Event>)>) -> Self {
120        Callback(Rc::new(move |state, event| {
121            yew_callback.emit((state, event));
122        }))
123    }
124}
125
126#[cfg(feature = "yew")]
127#[cfg_attr(docsrs, doc(cfg(feature = "yew")))]
128impl<State, Event> From<yew::Callback<()>> for Callback<State, Event>
129where
130    State: 'static,
131    Event: 'static,
132{
133    fn from(yew_callback: yew::Callback<()>) -> Self {
134        Callback(Rc::new(move |_, _| {
135            yew_callback.emit(());
136        }))
137    }
138}