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}