From 8e54a89a742987753d6440f3e7f309d305e426db Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 17 Jul 2023 17:23:15 -0700 Subject: [PATCH] fix use_server_future --- .../examples/axum-hello-world/Cargo.toml | 1 + .../examples/axum-hello-world/src/main.rs | 21 ++++----- .../examples/salvo-hello-world/Cargo.toml | 4 ++ .../examples/salvo-hello-world/src/main.rs | 46 +++++++++++++------ .../examples/warp-hello-world/Cargo.toml | 4 ++ .../examples/warp-hello-world/src/main.rs | 46 +++++++++++++------ packages/fullstack/src/hooks/server_future.rs | 46 +++++++++++-------- .../fullstack/src/html_storage/deserialize.rs | 35 +++++++++++--- packages/fullstack/src/render.rs | 3 +- 9 files changed, 141 insertions(+), 65 deletions(-) diff --git a/packages/fullstack/examples/axum-hello-world/Cargo.toml b/packages/fullstack/examples/axum-hello-world/Cargo.toml index b98d9f1a1..502d75bae 100644 --- a/packages/fullstack/examples/axum-hello-world/Cargo.toml +++ b/packages/fullstack/examples/axum-hello-world/Cargo.toml @@ -18,6 +18,7 @@ tower-http = { version = "0.4.1", features = ["auth"] } simple_logger = "4.2.0" wasm-logger = "0.2.0" log.workspace = true +reqwest = "0.11.18" [features] default = [] diff --git a/packages/fullstack/examples/axum-hello-world/src/main.rs b/packages/fullstack/examples/axum-hello-world/src/main.rs index 09bdcf3ce..cabe4b00e 100644 --- a/packages/fullstack/examples/axum-hello-world/src/main.rs +++ b/packages/fullstack/examples/axum-hello-world/src/main.rs @@ -23,16 +23,13 @@ fn app(cx: Scope) -> Element { fn Child(cx: Scope) -> Element { let state = use_server_future(cx, (), |()| async move { - #[cfg(not(feature = "ssr"))] - panic!(); - #[cfg(feature = "ssr")] - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - return 1; - })?; - - log::info!("running child"); - let state = state.value(); - log::info!("child state: {:?}", state); + loop { + if let Ok(res) = get_server_data().await { + break res; + } + } + })? + .value(); let mut count = use_state(cx, || 0); let text = use_state(cx, || "...".to_string()); @@ -72,12 +69,12 @@ async fn post_server_data(data: String) -> Result<(), ServerFnError> { #[server(GetServerData)] async fn get_server_data() -> Result { - Ok("Hello from the server!".to_string()) + Ok(reqwest::get("https://httpbin.org/ip").await?.text().await?) } fn main() { #[cfg(feature = "web")] - wasm_logger::init(wasm_logger::Config::default()); + wasm_logger::init(wasm_logger::Config::new(log::Level::Trace)); #[cfg(feature = "ssr")] simple_logger::SimpleLogger::new().init().unwrap(); diff --git a/packages/fullstack/examples/salvo-hello-world/Cargo.toml b/packages/fullstack/examples/salvo-hello-world/Cargo.toml index b85651f06..977ad7843 100644 --- a/packages/fullstack/examples/salvo-hello-world/Cargo.toml +++ b/packages/fullstack/examples/salvo-hello-world/Cargo.toml @@ -14,6 +14,10 @@ tokio = { workspace = true, features = ["full"], optional = true } serde = "1.0.159" salvo = { version = "0.37.9", optional = true } execute = "0.2.12" +reqwest = "0.11.18" +simple_logger = "4.2.0" +log.workspace = true +wasm-logger = "0.2.0" [features] default = [] diff --git a/packages/fullstack/examples/salvo-hello-world/src/main.rs b/packages/fullstack/examples/salvo-hello-world/src/main.rs index 9e5649efc..765b1836a 100644 --- a/packages/fullstack/examples/salvo-hello-world/src/main.rs +++ b/packages/fullstack/examples/salvo-hello-world/src/main.rs @@ -7,25 +7,37 @@ #![allow(non_snake_case, unused)] use dioxus::prelude::*; -use dioxus_fullstack::prelude::*; +use dioxus_fullstack::{launch, prelude::*}; use serde::{Deserialize, Serialize}; -fn main() { - launch!(@([127, 0, 0, 1], 8080), app, (AppProps { count: 5 }), { - incremental: IncrementalRendererConfig::default().invalidate_after(std::time::Duration::from_secs(120)), - }); -} - #[derive(Props, PartialEq, Debug, Default, Serialize, Deserialize, Clone)] struct AppProps { count: i32, } fn app(cx: Scope) -> Element { - let mut count = use_state(cx, || cx.props.count); + render! { + Child {} + } +} + +fn Child(cx: Scope) -> Element { + let state = use_server_future(cx, (), |()| async move { + loop { + if let Ok(res) = get_server_data().await { + break res; + } + } + })? + .value(); + + let mut count = use_state(cx, || 0); let text = use_state(cx, || "...".to_string()); cx.render(rsx! { + div { + "Server state: {state}" + } h1 { "High-Five counter: {count}" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } @@ -40,7 +52,7 @@ fn app(cx: Scope) -> Element { } } }, - "Run a server function" + "Run a server function!" } "Server said: {text}" }) @@ -48,15 +60,23 @@ fn app(cx: Scope) -> Element { #[server(PostServerData)] async fn post_server_data(data: String) -> Result<(), ServerFnError> { - // The server context contains information about the current request and allows you to modify the response. - let cx = server_context(); println!("Server received: {}", data); - println!("Request parts are {:?}", cx.request_parts()); Ok(()) } #[server(GetServerData)] async fn get_server_data() -> Result { - Ok("Hello from the server!".to_string()) + Ok(reqwest::get("https://httpbin.org/ip").await?.text().await?) +} + +fn main() { + #[cfg(feature = "web")] + wasm_logger::init(wasm_logger::Config::new(log::Level::Trace)); + #[cfg(feature = "ssr")] + simple_logger::SimpleLogger::new().init().unwrap(); + + launch!(@([127, 0, 0, 1], 8080), app, { + serve_cfg: ServeConfigBuilder::new(app, AppProps { count: 0 }), + }); } diff --git a/packages/fullstack/examples/warp-hello-world/Cargo.toml b/packages/fullstack/examples/warp-hello-world/Cargo.toml index 3c21372a9..d9e82b4c4 100644 --- a/packages/fullstack/examples/warp-hello-world/Cargo.toml +++ b/packages/fullstack/examples/warp-hello-world/Cargo.toml @@ -14,6 +14,10 @@ tokio = { workspace = true, features = ["full"], optional = true } serde = "1.0.159" warp = { version = "0.3.3", optional = true } execute = "0.2.12" +reqwest = "0.11.18" +simple_logger = "4.2.0" +log.workspace = true +wasm-logger = "0.2.0" [features] default = [] diff --git a/packages/fullstack/examples/warp-hello-world/src/main.rs b/packages/fullstack/examples/warp-hello-world/src/main.rs index 9e5649efc..765b1836a 100644 --- a/packages/fullstack/examples/warp-hello-world/src/main.rs +++ b/packages/fullstack/examples/warp-hello-world/src/main.rs @@ -7,25 +7,37 @@ #![allow(non_snake_case, unused)] use dioxus::prelude::*; -use dioxus_fullstack::prelude::*; +use dioxus_fullstack::{launch, prelude::*}; use serde::{Deserialize, Serialize}; -fn main() { - launch!(@([127, 0, 0, 1], 8080), app, (AppProps { count: 5 }), { - incremental: IncrementalRendererConfig::default().invalidate_after(std::time::Duration::from_secs(120)), - }); -} - #[derive(Props, PartialEq, Debug, Default, Serialize, Deserialize, Clone)] struct AppProps { count: i32, } fn app(cx: Scope) -> Element { - let mut count = use_state(cx, || cx.props.count); + render! { + Child {} + } +} + +fn Child(cx: Scope) -> Element { + let state = use_server_future(cx, (), |()| async move { + loop { + if let Ok(res) = get_server_data().await { + break res; + } + } + })? + .value(); + + let mut count = use_state(cx, || 0); let text = use_state(cx, || "...".to_string()); cx.render(rsx! { + div { + "Server state: {state}" + } h1 { "High-Five counter: {count}" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } @@ -40,7 +52,7 @@ fn app(cx: Scope) -> Element { } } }, - "Run a server function" + "Run a server function!" } "Server said: {text}" }) @@ -48,15 +60,23 @@ fn app(cx: Scope) -> Element { #[server(PostServerData)] async fn post_server_data(data: String) -> Result<(), ServerFnError> { - // The server context contains information about the current request and allows you to modify the response. - let cx = server_context(); println!("Server received: {}", data); - println!("Request parts are {:?}", cx.request_parts()); Ok(()) } #[server(GetServerData)] async fn get_server_data() -> Result { - Ok("Hello from the server!".to_string()) + Ok(reqwest::get("https://httpbin.org/ip").await?.text().await?) +} + +fn main() { + #[cfg(feature = "web")] + wasm_logger::init(wasm_logger::Config::new(log::Level::Trace)); + #[cfg(feature = "ssr")] + simple_logger::SimpleLogger::new().init().unwrap(); + + launch!(@([127, 0, 0, 1], 8080), app, { + serve_cfg: ServeConfigBuilder::new(app, AppProps { count: 0 }), + }); } diff --git a/packages/fullstack/src/hooks/server_future.rs b/packages/fullstack/src/hooks/server_future.rs index 2e3789ab7..9a13df235 100644 --- a/packages/fullstack/src/hooks/server_future.rs +++ b/packages/fullstack/src/hooks/server_future.rs @@ -4,6 +4,7 @@ use std::any::Any; use std::cell::Cell; use std::cell::Ref; use std::cell::RefCell; +use std::fmt::Debug; use std::future::Future; use std::rc::Rc; use std::sync::Arc; @@ -27,7 +28,7 @@ pub fn use_server_future( future: impl FnOnce(D::Out) -> F, ) -> Option<&UseServerFuture> where - T: 'static + Serialize + DeserializeOwned, + T: 'static + Serialize + DeserializeOwned + Debug, F: Future + 'static, D: UseFutureDep, { @@ -39,7 +40,24 @@ where dependencies: Vec::new(), }); - let first_run = { state.value.borrow().as_ref().is_none() }; + let first_run = { state.value.borrow().as_ref().is_none() && state.task.get().is_none() }; + + #[cfg(not(feature = "ssr"))] + { + if first_run { + match crate::html_storage::deserialize::take_server_data() { + Some(data) => { + log::trace!("Loaded {data:?} from server"); + *state.value.borrow_mut() = Some(Box::new(data)); + state.needs_regen.set(false); + return Some(state); + } + None => { + log::trace!("Failed to load from server... running future"); + } + }; + } + } if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen.get() { // We don't need regen anymore @@ -70,10 +88,7 @@ where } #[cfg(not(feature = "ssr"))] { - data = match crate::html_storage::deserialize::take_server_data() { - Some(data) => data, - None => fut.await, - }; + data = fut.await; } *value.borrow_mut() = Some(Box::new(data)); @@ -82,20 +97,17 @@ where } if first_run { - log::trace!("Suspending first run of use_server_future"); - cx.suspend(); + #[cfg(feature = "ssr")] + { + log::trace!("Suspending first run of use_server_future"); + cx.suspend(); + } None } else { Some(state) } } -pub enum FutureState<'a, T> { - Pending, - Complete(&'a T), - Regenerating(&'a T), // the old value -} - pub struct UseServerFuture { update: Arc, needs_regen: Cell, @@ -104,12 +116,6 @@ pub struct UseServerFuture { value: Rc>>>, } -pub enum UseFutureState<'a, T> { - Pending, - Complete(&'a T), - Reloading(&'a T), -} - impl UseServerFuture { /// Restart the future with new dependencies. /// diff --git a/packages/fullstack/src/html_storage/deserialize.rs b/packages/fullstack/src/html_storage/deserialize.rs index 3a0fc51df..822b45076 100644 --- a/packages/fullstack/src/html_storage/deserialize.rs +++ b/packages/fullstack/src/html_storage/deserialize.rs @@ -7,19 +7,42 @@ use super::HTMLDataCursor; #[allow(unused)] pub(crate) fn serde_from_bytes(string: &[u8]) -> Option { - let decompressed = STANDARD.decode(string).ok()?; + let decompressed = match STANDARD.decode(string) { + Ok(bytes) => bytes, + Err(err) => { + log::error!("Failed to decode base64: {}", err); + return None; + } + }; - postcard::from_bytes(&decompressed).ok() + match postcard::from_bytes(&decompressed) { + Ok(data) => Some(data), + Err(err) => { + log::error!("Failed to deserialize: {}", err); + None + } + } } static SERVER_DATA: once_cell::sync::Lazy> = once_cell::sync::Lazy::new(|| { #[cfg(target_arch = "wasm32")] { - let attribute = web_sys::window()? - .document()? - .get_element_by_id("dioxus-storage-data")? - .get_attribute("data-serialized")?; + let window = web_sys::window()?.document()?; + let element = match window.get_element_by_id("dioxus-storage-data") { + Some(element) => element, + None => { + log::error!("Failed to get element by id: dioxus-storage-data"); + return None; + } + }; + let attribute = match element.get_attribute("data-serialized") { + Some(attribute) => attribute, + None => { + log::error!("Failed to get attribute: data-serialized"); + return None; + } + }; let data: super::HTMLData = serde_from_bytes(attribute.as_bytes())?; diff --git a/packages/fullstack/src/render.rs b/packages/fullstack/src/render.rs index c6a13c347..9699a5eb3 100644 --- a/packages/fullstack/src/render.rs +++ b/packages/fullstack/src/render.rs @@ -12,7 +12,7 @@ use serde::Serialize; use std::sync::RwLock; use tokio::task::spawn_blocking; -use crate::{prelude::*, server_context::with_server_context}; +use crate::prelude::*; use dioxus::prelude::*; enum SsrRendererPool { @@ -300,6 +300,7 @@ impl dioxus_ssr::incremental::Wrap } /// A rendered response from the server. +#[derive(Debug)] pub struct RenderResponse { pub(crate) html: String, pub(crate) freshness: RenderFreshness,