yewdux_functional/
lib.rs

1use std::{cell::RefCell, ops::Deref, rc::Rc};
2
3use yew::{functional::*, prelude::*};
4use yewdux::dispatch::Dispatch;
5use yewdux::store::Store;
6
7/// Reference to a store's state and dispatch.
8pub struct StoreRef<T: Store> {
9    state: UseStateHandle<Option<Rc<T::Model>>>,
10    dispatch: Rc<RefCell<Dispatch<T>>>,
11    #[allow(dead_code)]
12    output: Option<Rc<RefCell<Dispatch<T>>>>,
13}
14
15impl<T: Store> StoreRef<T> {
16    pub fn dispatch<'a>(&'a self) -> impl Deref<Target = Dispatch<T>> + 'a {
17        self.dispatch.borrow()
18    }
19
20    pub fn state(&self) -> Option<&Rc<T::Model>> {
21        self.state.as_ref()
22    }
23
24    pub fn on_output(mut self, on_output: impl Fn(T::Output) + 'static) -> Self {
25        self.output = Some(use_mut_ref(move || {
26            Dispatch::bridge(Default::default(), on_output.into())
27        }));
28        self
29    }
30}
31
32/// This hook allows accessing the state of a store. When the store is modified, a re-render is automatically triggered.
33/// This hook also accepts a callback that is triggered for state output. To only receive state, use [`use_store_state`] instead.
34///
35/// This function returns the state of the store.
36///
37/// # Example
38/// ```ignore
39/// # use yew_functional::function_component;
40/// # use yew::prelude::*;
41/// use yewdux_functional::use_store;
42///
43/// #[derive(Default, Clone)]
44/// struct State {
45///     count: u32,
46/// }
47///
48/// #[function_component(App)]
49/// fn app() -> Html {
50///     let store = use_store::<BasicStore<State>>();
51///     let count = store.state().map(|s| s.count).unwrap_or(0);
52///     let onclick = store.dispatch().reduce_callback(|s| s.count += 1);
53///     html! {
54///         <>
55///         <p>{ count }</p>
56///         <button onclick=onclick>{"+1"}</button>
57///         </>
58///     }
59/// }
60/// ```
61pub fn use_store<T: Store>() -> StoreRef<T> {
62    let state = use_state(|| None);
63
64    let dispatch = {
65        let state = state.clone();
66        // persist the Dispatch across renders
67        use_mut_ref(move || {
68            let on_state = Callback::from(move |new_state| {
69                state.set(Some(new_state));
70            });
71
72            Dispatch::<T>::bridge_state(on_state)
73        })
74    };
75
76    StoreRef {
77        state,
78        dispatch,
79        output: None,
80    }
81}
82
83/// This hook allows getting a [`Dispatch`] to the store.
84///
85/// Do not use the `state` method on the [`Dispatch`]. The dispatch should only be used to create callbacks.
86/// The proper way to access the state is via the [`use_store`] hook.
87///
88/// # Example
89/// ```ignore
90/// # use yew_functional::function_component;
91/// # use yew::prelude::*;
92/// use yewdux::use_dispatch;
93///
94/// #[function_component(UseDispatch)]
95/// fn dispatch() -> Html {
96///     let dispatch = use_dispatch::<CounterStore>();
97///     
98///     html! {
99///         <button onclick=dispatch.callback(|_| Input::Increment)>{ "Increment" }</button>
100///     }
101/// }
102/// ```
103pub fn use_dispatch<T: Store>() -> impl Deref<Target = Dispatch<T>> {
104    // persist the Dispatch across renders
105    let dispatch = use_state(Dispatch::<T>::new);
106
107    dispatch
108}