forked from yewstack/yew
-
Notifications
You must be signed in to change notification settings - Fork 0
/
feat_hydration.rs
122 lines (100 loc) · 3.72 KB
/
feat_hydration.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! The client-side rendering variant. This is used for client side rendering.
use std::marker::PhantomData;
use std::rc::Rc;
use serde::de::DeserializeOwned;
use serde::Serialize;
use wasm_bindgen::JsValue;
use super::PreparedStateBase;
use crate::functional::{use_state, Hook, HookContext};
use crate::platform::spawn_local;
use crate::suspense::{Suspension, SuspensionResult};
#[cfg(target_arch = "wasm32")]
async fn decode_base64(s: &str) -> Result<Vec<u8>, JsValue> {
use gloo_utils::window;
use js_sys::Uint8Array;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
let fetch_promise = window().fetch_with_str(s);
let content_promise = JsFuture::from(fetch_promise)
.await
.and_then(|m| m.dyn_into::<web_sys::Response>())
.and_then(|m| m.array_buffer())?;
let content_array = JsFuture::from(content_promise)
.await
.as_ref()
.map(Uint8Array::new)?;
Ok(content_array.to_vec())
}
#[cfg(not(target_arch = "wasm32"))]
async fn decode_base64(_s: &str) -> Result<Vec<u8>, JsValue> {
unreachable!("this function is not callable under non-wasm targets!");
}
#[doc(hidden)]
pub fn use_prepared_state<T, D>(deps: Rc<D>) -> impl Hook<Output = SuspensionResult<Option<Rc<T>>>>
where
D: Serialize + DeserializeOwned + PartialEq + 'static,
T: Serialize + DeserializeOwned + 'static,
{
struct HookProvider<T, D>
where
D: Serialize + DeserializeOwned + PartialEq + 'static,
T: Serialize + DeserializeOwned + 'static,
{
_marker: PhantomData<T>,
deps: Rc<D>,
}
impl<T, D> Hook for HookProvider<T, D>
where
D: Serialize + DeserializeOwned + PartialEq + 'static,
T: Serialize + DeserializeOwned + 'static,
{
type Output = SuspensionResult<Option<Rc<T>>>;
fn run(self, ctx: &mut HookContext) -> Self::Output {
let data = use_state(|| {
let (s, handle) = Suspension::new();
(
SuspensionResult::<(Option<Rc<T>>, Option<Rc<D>>)>::Err(s),
Some(handle),
)
})
.run(ctx);
let state = {
let data = data.clone();
ctx.next_prepared_state(move |_re_render, buf| -> PreparedStateBase<T, D> {
if let Some(buf) = buf {
let buf = format!("data:application/octet-binary;base64,{}", buf);
spawn_local(async move {
let buf = decode_base64(&buf)
.await
.expect("failed to deserialize state");
let (state, deps) =
bincode::deserialize::<(Option<T>, Option<D>)>(&buf)
.map(|(state, deps)| (state.map(Rc::new), deps.map(Rc::new)))
.expect("failed to deserialize state");
data.set((Ok((state, deps)), None));
});
}
PreparedStateBase {
#[cfg(feature = "ssr")]
state: None,
#[cfg(feature = "ssr")]
deps: None,
has_buf: buf.is_some(),
_marker: PhantomData,
}
})
};
if state.has_buf {
let (data, deps) = data.0.clone()?;
if deps.as_deref() == Some(&self.deps) {
return Ok(data);
}
}
Ok(None)
}
}
HookProvider::<T, D> {
_marker: PhantomData,
deps,
}
}