codify "platform" into resolution logic, server-fn/fullstack overhaul (#4596)

overhaul dioxus fullstack using a new implementation of server_fn

This PR is a complete rewrite of dioxus fullstack designed to improve compatibility with axum and to fix a number of compilation errors based on feature flag unification plumbing through macros.
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2025-10-03 18:26:19 -07:00
1 parent 8421c3aef0
commit f2b1c6d97f
424 files changed
+17284 -6624

No files matched your search

@@ -0,0 +1,65 @@
//! Backgrounded futures example
//!
//! This showcases how use_future, use_memo, and use_effect will stop running if the component returns early.
//! Generally you should avoid using early returns around hooks since most hooks are not properly designed to
//! handle early returns. However, use_future *does* pause the future when the component returns early, and so
//! hooks that build on top of it like use_memo and use_effect will also pause.
//!
//! This example is more of a demonstration of the behavior than a practical use case, but it's still interesting to see.
use async_std::task::sleep;
use dioxus::prelude::*;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
let mut show_child = use_signal(|| true);
let mut count = use_signal(|| 0);
let child = use_memo(move || {
rsx! {
Child { count }
}
});
rsx! {
// Some toggle/controls to show the child or increment the count
button { onclick: move |_| show_child.toggle(), "Toggle child" }
button { onclick: move |_| count += 1, "Increment count" }
if show_child() {
{child()}
}
}
}
#[component]
fn Child(count: WriteSignal<i32>) -> Element {
let mut early_return = use_signal(|| false);
let early = rsx! {
button { onclick: move |_| early_return.toggle(), "Toggle {early_return} early return" }
};
if early_return() {
return early;
}
use_future(move || async move {
loop {
sleep(std::time::Duration::from_millis(100)).await;
println!("Child")
}
});
use_effect(move || println!("Child count: {}", count()));
rsx! {
div {
"Child component"
{early}
}
}
}
+42
View File
@@ -0,0 +1,42 @@
//! A simple little clock that updates the time every few milliseconds.
use async_std::task::sleep;
use dioxus::prelude::*;
use web_time::Instant;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
let mut millis = use_signal(|| 0);
use_future(move || async move {
// Save our initial time
let start = Instant::now();
loop {
sleep(std::time::Duration::from_millis(27)).await;
// Update the time, using a more precise approach of getting the duration since we started the timer
millis.set(start.elapsed().as_millis() as i64);
}
});
// Format the time as a string
// This is rather cheap so it's fine to leave it in the render function
let time = format!(
"{:02}:{:02}:{:03}",
millis() / 1000 / 60 % 60,
millis() / 1000 % 60,
millis() % 1000
);
rsx! {
document::Stylesheet { href: asset!("/examples/assets/clock.css") }
div { id: "app",
div { id: "title", "Carpe diem 🎉" }
div { id: "clock-display", "{time}" }
}
}
}
+49
View File
@@ -0,0 +1,49 @@
//! A simple example that shows how to use the use_future hook to run a background task.
//!
//! use_future won't return a value, analogous to use_effect.
//! If you want to return a value from a future, use use_resource instead.
use async_std::task::sleep;
use dioxus::prelude::*;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
let mut count = use_signal(|| 0);
// use_future is a non-reactive hook that simply runs a future in the background.
// You can use the UseFuture handle to pause, resume, restart, or cancel the future.
use_future(move || async move {
loop {
sleep(std::time::Duration::from_millis(200)).await;
count += 1;
}
});
// use_effect is a reactive hook that runs a future when signals captured by its reactive context
// are modified. This is similar to use_effect in React and is useful for running side effects
// that depend on the state of your component.
//
// Generally, we recommend performing async work in event as a reaction to a user event.
use_effect(move || {
spawn(async move {
sleep(std::time::Duration::from_secs(5)).await;
count.set(100);
});
});
// You can run futures directly from event handlers as well. Note that if the event handler is
// fired multiple times, the future will be spawned multiple times.
rsx! {
h1 { "Current count: {count}" }
button {
onclick: move |_| async move {
sleep(std::time::Duration::from_millis(200)).await;
count.set(0);
},
"Slowly reset the count"
}
}
}
+37
View File
@@ -0,0 +1,37 @@
//! Handle async streams using use_future and awaiting the next value.
use async_std::task::sleep;
use dioxus::prelude::*;
use futures_util::{Stream, StreamExt, future, stream};
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
let mut count = use_signal(|| 10);
use_future(move || async move {
// Create the stream.
// This could be a network request, a file read, or any other async operation.
let mut stream = some_stream();
// Await the next value from the stream.
while let Some(second) = stream.next().await {
count.set(second);
}
});
rsx! {
h1 { "High-Five counter: {count}" }
}
}
fn some_stream() -> std::pin::Pin<Box<dyn Stream<Item = i32>>> {
Box::pin(
stream::once(future::ready(0)).chain(stream::iter(1..).then(|second| async move {
sleep(std::time::Duration::from_secs(1)).await;
second
})),
)
}
+62
View File
@@ -0,0 +1,62 @@
//! Suspense in Dioxus
//!
//! Suspense allows components to bubble up loading states to parent components, simplifying data fetching.
use dioxus::prelude::*;
fn main() {
dioxus::launch(app)
}
fn app() -> Element {
rsx! {
div {
h1 { "Dogs are very important" }
p {
"The dog or domestic dog (Canis familiaris[4][5] or Canis lupus familiaris[5])"
"is a domesticated descendant of the wolf which is characterized by an upturning tail."
"The dog derived from an ancient, extinct wolf,[6][7] and the modern grey wolf is the"
"dog's nearest living relative.[8] The dog was the first species to be domesticated,[9][8]"
"by hunter–gatherers over 15,000 years ago,[7] before the development of agriculture.[1]"
}
h3 { "Illustrious Dog Photo" }
ErrorBoundary { handle_error: |_| rsx! { p { "Error loading doggos" } },
SuspenseBoundary { fallback: move |_| rsx! { "Loading doggos..." },
Doggo {}
}
}
}
}
}
#[component]
fn Doggo() -> Element {
// `use_loader` returns a Result<Loader<T>, Loading>. Loading can either be "Pending" or "Failed".
// When we use the `?` operator, the pending/error state will be thrown to the nearest Suspense or Error boundary.
//
// During SSR, `use_loader` will serialize the contents of the fetch, and during hydration, the client will
// use the pre-fetched data instead of re-fetching to render.
let mut dog = use_loader(move || async move {
#[derive(serde::Deserialize, serde::Serialize, PartialEq)]
struct DogApi {
message: String,
}
reqwest::get("https://dog.ceo/api/breeds/image/random/")
.await
.unwrap()
.json::<DogApi>()
.await
})?;
rsx! {
button { onclick: move |_| dog.restart(), "Click to fetch another doggo" }
div {
img {
max_width: "500px",
max_height: "500px",
src: "{dog.read().message}"
}
}
}
}