yew/functional/hooks/use_prepared_state/mod.rs
1#[cfg(feature = "hydration")]
2pub(super) mod feat_hydration;
3#[cfg(all(feature = "hydration", feature = "ssr"))]
4mod feat_hydration_ssr;
5#[cfg(not(any(feature = "hydration", feature = "ssr")))]
6pub(super) mod feat_none;
7#[cfg(feature = "ssr")]
8mod feat_ssr;
9
10#[cfg(all(feature = "hydration", not(feature = "ssr")))]
11pub use feat_hydration::*;
12#[cfg(all(feature = "ssr", feature = "hydration"))]
13pub use feat_hydration_ssr::*;
14#[cfg(not(any(feature = "hydration", feature = "ssr")))]
15pub use feat_none::*;
16#[cfg(all(feature = "ssr", not(feature = "hydration")))]
17pub use feat_ssr::*;
18/// Use a state prepared on the server side and its value is sent to the client side during
19/// hydration.
20///
21/// The component sees the same value on the server side and client side if the component is
22/// hydrated.
23///
24/// It accepts a closure as the first argument and a dependency type as the second argument.
25/// It returns `SuspensionResult<Option<Rc<T>>>`.
26///
27/// During hydration, it will only return `Ok(Some(Rc<T>))` if the component is hydrated from a
28/// server-side rendering artifact and its dependency value matches.
29///
30/// `let state = use_prepared_state!(|deps| -> ReturnType { ... }, deps)?;`
31///
32/// It has the following signature:
33///
34/// ```
35/// # use serde::de::DeserializeOwned;
36/// # use serde::Serialize;
37/// # use std::rc::Rc;
38/// use yew::prelude::*;
39/// use yew::suspense::SuspensionResult;
40///
41/// #[hook]
42/// pub fn use_prepared_state<T, D, F>(deps: D, f: F) -> SuspensionResult<Option<Rc<T>>>
43/// where
44/// D: Serialize + DeserializeOwned + PartialEq + 'static,
45/// T: Serialize + DeserializeOwned + 'static,
46/// F: FnOnce(Rc<D>) -> T,
47/// # { todo!() }
48/// ```
49///
50/// The first argument can also be an [async closure](https://github.com/rust-lang/rust/issues/62290).
51///
52/// `let state = use_prepared_state!(async |deps| -> ReturnType { ... }, deps)?;`
53///
54/// When accepting an async closure, it has the following signature:
55///
56/// ```
57/// # use serde::de::DeserializeOwned;
58/// # use serde::Serialize;
59/// # use std::rc::Rc;
60/// # use std::future::Future;
61/// use yew::prelude::*;
62/// use yew::suspense::SuspensionResult;
63///
64/// #[hook]
65/// pub fn use_prepared_state<T, D, F, U>(
66/// deps: D,
67/// f: F,
68/// ) -> SuspensionResult<Option<Rc<T>>>
69/// where
70/// D: Serialize + DeserializeOwned + PartialEq + 'static,
71/// T: Serialize + DeserializeOwned + 'static,
72/// F: FnOnce(Rc<D>) -> U,
73/// U: 'static + Future<Output = T>,
74/// # { todo!() }
75/// ```
76///
77/// During server-side rendering, a value of type `T` will be calculated from the first
78/// closure.
79///
80/// If the bundle is compiled without server-side rendering, the closure will be stripped
81/// automatically.
82///
83/// # Note
84///
85/// You MUST denote the return type of the closure with `|deps| -> ReturnType { ... }`. This
86/// type is used during client side rendering to deserialize the state prepared on the server
87/// side.
88///
89/// Whilst async closure is an unstable feature, the procedural macro will rewrite this to a
90/// closure that returns an async block automatically. You can use this hook with async closure
91/// in stable Rust.
92pub use use_prepared_state_macro as use_prepared_state;
93// With SSR.
94#[doc(hidden)]
95#[cfg(feature = "ssr")]
96pub use yew_macro::use_prepared_state_with_closure as use_prepared_state_macro;
97// Without SSR.
98#[doc(hidden)]
99#[cfg(not(feature = "ssr"))]
100pub use yew_macro::use_prepared_state_without_closure as use_prepared_state_macro;
101
102#[cfg(any(feature = "hydration", feature = "ssr"))]
103mod feat_any_hydration_ssr {
104 use std::marker::PhantomData;
105 #[cfg(feature = "ssr")]
106 use std::rc::Rc;
107
108 use serde::de::DeserializeOwned;
109 use serde::Serialize;
110
111 use crate::functional::PreparedState;
112
113 pub(super) struct PreparedStateBase<T, D>
114 where
115 D: Serialize + DeserializeOwned + PartialEq + 'static,
116 T: Serialize + DeserializeOwned + 'static,
117 {
118 #[cfg(feature = "ssr")]
119 pub state: Option<Rc<T>>,
120 #[cfg(feature = "ssr")]
121 pub deps: Option<Rc<D>>,
122 #[cfg(feature = "hydration")]
123 pub has_buf: bool,
124 pub _marker: PhantomData<(T, D)>,
125 }
126
127 impl<T, D> PreparedState for PreparedStateBase<T, D>
128 where
129 D: Serialize + DeserializeOwned + PartialEq + 'static,
130 T: Serialize + DeserializeOwned + 'static,
131 {
132 #[cfg(feature = "ssr")]
133 fn prepare(&self) -> String {
134 use base64ct::{Base64, Encoding};
135
136 let state = bincode::serialize(&(self.state.as_deref(), self.deps.as_deref()))
137 .expect("failed to prepare state");
138
139 Base64::encode_string(&state)
140 }
141 }
142}
143
144#[cfg(any(feature = "hydration", feature = "ssr"))]
145use feat_any_hydration_ssr::PreparedStateBase;