fix: properly support concurrent loading without breaking changes to ChooseView

This commit is contained in:
Greg Johnston
2025-07-19 08:52:06 -04:00
parent 232b603a25
commit 0c67f7d389
4 changed files with 37 additions and 106 deletions

View File

@@ -145,10 +145,8 @@ where
provide_context(url.clone());
provide_context(Matched(ArcMemo::from(matched.clone())));
let data = view.data();
ScopedFuture::new(async move {
OwnedView::new(view.choose(data).await)
OwnedView::new(view.choose().await)
})
}));
@@ -301,16 +299,13 @@ where
let view = OwnedView::new(
if let Some(set_is_routing) = set_is_routing {
set_is_routing.set(true);
let value = AsyncTransition::run(|| {
let data = view.data();
view.choose(data)
})
.await;
let value =
AsyncTransition::run(|| view.choose())
.await;
set_is_routing.set(false);
value
} else {
let data = view.data();
view.choose(data).await
view.choose().await
},
);
@@ -526,11 +521,7 @@ where
provide_context(params_memo);
provide_context(Matched(ArcMemo::from(matched)));
let data = view.data();
ScopedFuture::new(
async move { view.choose(data).await },
)
ScopedFuture::new(async move { view.choose().await })
})
.now_or_never()
.expect("async route used in SSR");
@@ -712,10 +703,8 @@ where
provide_context(url.clone());
provide_context(Matched(ArcMemo::from(matched.clone())));
let data = view.data();
ScopedFuture::new(async move {
OwnedView::new(view.choose(data).await)
OwnedView::new(view.choose().await)
})
}));
@@ -810,10 +799,8 @@ where
provide_context(url.clone());
provide_context(Matched(ArcMemo::from(matched.clone())));
let data = view.data();
ScopedFuture::new(async move {
OwnedView::new(view.choose(data).await)
OwnedView::new(view.choose().await)
})
}));

View File

@@ -8,8 +8,7 @@ pub struct AnyChooseView {
value: Erased,
clone: fn(&Erased) -> AnyChooseView,
#[allow(clippy::type_complexity)]
choose: fn(Erased, Erased) -> Pin<Box<dyn Future<Output = AnyView>>>,
data: fn(&Erased) -> Erased,
choose: fn(Erased) -> Pin<Box<dyn Future<Output = AnyView>>>,
preload: for<'a> fn(&'a Erased) -> Pin<Box<dyn Future<Output = ()> + 'a>>,
}
@@ -27,12 +26,8 @@ impl AnyChooseView {
fn choose<T: ChooseView>(
value: Erased,
data: Erased,
) -> Pin<Box<dyn Future<Output = AnyView>>> {
value
.into_inner::<T>()
.choose(data.into_inner::<T::Data>())
.boxed_local()
value.into_inner::<T>().choose().boxed_local()
}
fn preload<'a, T: ChooseView>(
@@ -41,32 +36,21 @@ impl AnyChooseView {
value.get_ref::<T>().preload().boxed_local()
}
fn data<T: ChooseView>(value: &Erased) -> Erased {
Erased::new(value.get_ref::<T>().data())
}
Self {
value: Erased::new(value),
clone: clone::<T>,
choose: choose::<T>,
data: data::<T>,
preload: preload::<T>,
}
}
}
impl ChooseView for AnyChooseView {
type Data = Erased;
async fn choose(self, data: Self::Data) -> AnyView {
(self.choose)(self.value, data).await
async fn choose(self) -> AnyView {
(self.choose)(self.value).await
}
async fn preload(&self) {
(self.preload)(&self.value).await;
}
fn data(&self) -> Self::Data {
(self.data)(&self.value)
}
}

View File

@@ -1,4 +1,5 @@
use either_of::*;
use leptos::prelude::{ArcStoredValue, WriteValue};
use std::{future::Future, marker::PhantomData};
use tachys::view::any_view::{AnyView, IntoAny};
@@ -6,13 +7,9 @@ pub trait ChooseView
where
Self: Send + Clone + 'static,
{
type Data: Send + 'static;
fn choose(self, data: Self::Data) -> impl Future<Output = AnyView>;
fn choose(self) -> impl Future<Output = AnyView>;
fn preload(&self) -> impl Future<Output = ()>;
fn data(&self) -> Self::Data;
}
impl<F, View> ChooseView for F
@@ -20,34 +17,26 @@ where
F: Fn() -> View + Send + Clone + 'static,
View: IntoAny,
{
type Data = ();
async fn choose(self, _data: ()) -> AnyView {
async fn choose(self) -> AnyView {
self().into_any()
}
async fn preload(&self) {}
fn data(&self) -> Self::Data {}
}
impl<T> ChooseView for Lazy<T>
where
T: LazyRoute,
T: Send + Sync + LazyRoute,
{
type Data = T;
async fn choose(self, data: T) -> AnyView {
async fn choose(self) -> AnyView {
let data = self.data.write_value().take().unwrap_or_else(T::data);
T::view(data).await
}
async fn preload(&self) {
*self.data.write_value() = Some(T::data());
T::preload().await;
}
fn data(&self) -> Self::Data {
T::data()
}
}
pub trait LazyRoute: Send + 'static {
@@ -63,11 +52,15 @@ pub trait LazyRoute: Send + 'static {
#[derive(Debug)]
pub struct Lazy<T> {
ty: PhantomData<T>,
data: ArcStoredValue<Option<T>>,
}
impl<T> Clone for Lazy<T> {
fn clone(&self) -> Self {
Self { ty: self.ty }
Self {
ty: self.ty,
data: self.data.clone(),
}
}
}
@@ -81,20 +74,17 @@ impl<T> Default for Lazy<T> {
fn default() -> Self {
Self {
ty: Default::default(),
data: ArcStoredValue::new(None),
}
}
}
impl ChooseView for () {
type Data = ();
async fn choose(self, _data: ()) -> AnyView {
async fn choose(self) -> AnyView {
().into_any()
}
async fn preload(&self) {}
fn data(&self) -> Self::Data {}
}
impl<A, B> ChooseView for Either<A, B>
@@ -102,15 +92,10 @@ where
A: ChooseView,
B: ChooseView,
{
type Data = Either<A::Data, B::Data>;
async fn choose(self, data: Self::Data) -> AnyView {
match (self, data) {
(Either::Left(f), Either::Left(d)) => f.choose(d).await.into_any(),
(Either::Right(f), Either::Right(d)) => {
f.choose(d).await.into_any()
}
_ => unreachable!(),
async fn choose(self) -> AnyView {
match self {
Either::Left(f) => f.choose().await.into_any(),
Either::Right(f) => f.choose().await.into_any(),
}
}
@@ -120,13 +105,6 @@ where
Either::Right(f) => f.preload().await,
}
}
fn data(&self) -> Self::Data {
match self {
Either::Left(f) => Either::Left(f.data()),
Either::Right(f) => Either::Right(f.data()),
}
}
}
macro_rules! tuples {
@@ -135,14 +113,11 @@ macro_rules! tuples {
where
$($ty: ChooseView,)*
{
type Data = $either<$($ty::Data),*>;
async fn choose(self, data: Self::Data) -> AnyView {
match (self, data) {
async fn choose(self) -> AnyView {
match self {
$(
($either::$ty(f), $either::$ty(d)) => f.choose(d).await.into_any(),
$either::$ty(f) => f.choose().await.into_any(),
)*
_ => unreachable!()
}
}
@@ -151,12 +126,6 @@ macro_rules! tuples {
$($either::$ty(f) => f.preload().await,)*
}
}
fn data(&self) -> Self::Data {
match self {
$($either::$ty(f) => $either::$ty(f.data()),)*
}
}
}
};
}

View File

@@ -692,7 +692,6 @@ where
provide_context(params.clone());
provide_context(url.clone());
provide_context(matched.clone());
let mut data = Some(view.data());
view.preload().await;
let child = outlet.child.clone();
*view_fn.lock().or_poisoned() =
@@ -707,8 +706,6 @@ where
owner_where_used.with({
let matched = matched.clone();
|| {
let data =
data.take().unwrap_or_else(|| view.data());
let child = child.clone();
Suspend::new(Box::pin(async move {
provide_context(child.clone());
@@ -716,7 +713,7 @@ where
provide_context(url.clone());
provide_context(matched.clone());
let view = SendWrapper::new(
ScopedFuture::new(view.choose(data)),
ScopedFuture::new(view.choose()),
);
let view = view.await;
let view = MatchedRoute(
@@ -855,6 +852,7 @@ where
let child = outlet.child.clone();
async move {
let child = child.clone();
view.preload().await;
*view_fn.lock().or_poisoned() =
Box::new(move |owner_where_used| {
let prev_owner = route_owner
@@ -879,18 +877,11 @@ where
ScopedFuture::new(async move {
if set_is_routing {
AsyncTransition::run(
|| {
let data =
view.data();
view.choose(
data,
)
},
|| view.choose(),
)
.await
} else {
let data = view.data();
view.choose(data).await
view.choose().await
}
})
}),