Bubble errors from the server to the client on suspense boundaries (#2772)
* Bubble errors from the server to the client on suspense boundaries * make resolving suspense boundaries O(n)
This commit is contained in:
1 parent
3003362b52
commit
b0a2e2418c
12 files changed
+190
-39
No files matched your search
@@ -1,6 +1,7 @@
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::io::Cursor;
|
||||
|
||||
use dioxus_core::CapturedError;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
thread_local! {
|
||||
@@ -37,6 +38,7 @@ fn remove_server_data() {
|
||||
|
||||
/// Data that is deserialized from the server during hydration
|
||||
pub(crate) struct HTMLDataCursor {
|
||||
error: Option<CapturedError>,
|
||||
data: Vec<Option<Vec<u8>>>,
|
||||
index: Cell<usize>,
|
||||
}
|
||||
@@ -47,11 +49,28 @@ impl HTMLDataCursor {
|
||||
Self::new(deserialized)
|
||||
}
|
||||
|
||||
/// Get the error if there is one
|
||||
pub(crate) fn error(&self) -> Option<CapturedError> {
|
||||
self.error.clone()
|
||||
}
|
||||
|
||||
fn new(data: Vec<Option<Vec<u8>>>) -> Self {
|
||||
Self {
|
||||
let mut myself = Self {
|
||||
error: None,
|
||||
data,
|
||||
index: Cell::new(0),
|
||||
}
|
||||
};
|
||||
|
||||
// The first item is always an error if it exists
|
||||
let error = myself
|
||||
.take::<Option<CapturedError>>()
|
||||
.ok()
|
||||
.flatten()
|
||||
.flatten();
|
||||
|
||||
myself.error = error;
|
||||
|
||||
myself
|
||||
}
|
||||
|
||||
pub fn take<T: DeserializeOwned>(&self) -> Result<Option<T>, TakeDataError> {
|
||||
|
||||
@@ -138,7 +138,12 @@ impl WebsysDom {
|
||||
self.interpreter.base().push_root(node);
|
||||
}
|
||||
|
||||
with_server_data(HTMLDataCursor::from_serialized(&data), || {
|
||||
let server_data = HTMLDataCursor::from_serialized(&data);
|
||||
// If the server serialized an error into the suspense boundary, throw it on the client so that it bubbles up to the nearest error boundary
|
||||
if let Some(error) = server_data.error() {
|
||||
dom.in_runtime(|| id.throw_error(error));
|
||||
}
|
||||
with_server_data(server_data, || {
|
||||
// rerun the scope with the new data
|
||||
SuspenseBoundaryProps::resolve_suspense(
|
||||
id,
|
||||
|
||||
@@ -24,7 +24,7 @@ use std::{panic, rc::Rc};
|
||||
|
||||
pub use crate::cfg::Config;
|
||||
use crate::hydration::SuspenseMessage;
|
||||
use dioxus_core::VirtualDom;
|
||||
use dioxus_core::{ScopeId, VirtualDom};
|
||||
use futures_util::{pin_mut, select, FutureExt, StreamExt};
|
||||
|
||||
mod cfg;
|
||||
@@ -98,6 +98,10 @@ pub async fn run(virtual_dom: VirtualDom, web_config: Config) -> ! {
|
||||
}
|
||||
let hydration_data = get_initial_hydration_data().to_vec();
|
||||
let server_data = HTMLDataCursor::from_serialized(&hydration_data);
|
||||
// If the server serialized an error into the root suspense boundary, throw it into the root scope
|
||||
if let Some(error) = server_data.error() {
|
||||
dom.in_runtime(|| ScopeId::APP.throw_error(error));
|
||||
}
|
||||
with_server_data(server_data, || {
|
||||
dom.rebuild(&mut websys_dom);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user