bounce/
provider.rs

1use anymap2::AnyMap;
2use yew::prelude::*;
3
4use crate::root_state::BounceRootState;
5
6/// Properties for [`BounceRoot`].
7#[derive(Properties, Debug, PartialEq, Clone)]
8pub struct BounceRootProps {
9    /// Children of a Bounce Root.
10    #[prop_or_default]
11    pub children: Children,
12
13    /// A callback that retrieves an `AnyMap` that contains initial states.
14    ///
15    /// States not provided will use `Default`.
16    ///
17    /// This only affects [`Atom`](macro@crate::Atom) and [`Slice`](macro@crate::Slice).
18    #[prop_or_default]
19    pub get_init_states: Option<Callback<(), AnyMap>>,
20}
21
22/// A `<BounceRoot />`.
23///
24/// For bounce states to function, A `<BounceRoot />` must present and registered as a context
25/// provider.
26///
27/// # Example
28///
29/// ```
30/// # use yew::prelude::*;
31/// # use bounce::prelude::*;
32/// # use bounce::BounceRoot;
33/// #[function_component(App)]
34/// fn app() -> Html {
35///     html! {
36///         <BounceRoot>
37///             // children...
38///         </BounceRoot>
39///     }
40/// }
41///
42/// ```
43#[function_component(BounceRoot)]
44pub fn bounce_root(props: &BounceRootProps) -> Html {
45    let BounceRootProps {
46        children,
47        get_init_states,
48    } = props.clone();
49
50    let root_state = (*use_state(move || {
51        let init_states = get_init_states.map(|m| m.emit(())).unwrap_or_default();
52        BounceRootState::new(init_states)
53    }))
54    .clone();
55
56    #[allow(clippy::redundant_clone)]
57    {
58        let root_state = root_state.clone();
59        use_effect_with((), move |_| {
60            // We clear all states manually.
61            move || {
62                root_state.clear();
63            }
64        });
65    }
66
67    #[allow(clippy::unused_unit, clippy::redundant_clone)]
68    {
69        let _root_state = root_state.clone();
70        let _ = use_transitive_state!((), move |_| -> () {
71            #[cfg(feature = "ssr")]
72            #[cfg(feature = "helmet")]
73            {
74                // Workaround to send helmet states back to static writer
75                use crate::helmet::StaticWriterState;
76
77                let states = _root_state.states();
78                let writer_state = states.get_atom_value::<StaticWriterState>();
79
80                if let Some(ref w) = writer_state.writer {
81                    w.send_helmet(
82                        states,
83                        writer_state.format_title.clone(),
84                        writer_state.default_title.clone(),
85                    );
86                }
87            }
88
89            // We drop the root state on SSR as well.
90            _root_state.clear();
91        });
92    }
93
94    html! {
95        <ContextProvider<BounceRootState> context={root_state}>{children}</ContextProvider<BounceRootState>>
96    }
97}