fix: require `Suspend::new()` to ensure the `Future` is properly scoped at creation time, not at render time
This commit is contained in:
parent
b24ae7a5e3
commit
93e6456e19
|
@ -204,11 +204,11 @@ mod view_implementations {
|
||||||
type State = RenderEffectState<SuspendState<T, R>>;
|
type State = RenderEffectState<SuspendState<T, R>>;
|
||||||
|
|
||||||
fn build(self) -> Self::State {
|
fn build(self) -> Self::State {
|
||||||
(move || Suspend(async move { self.await })).build()
|
(move || Suspend::new(async move { self.await })).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rebuild(self, state: &mut Self::State) {
|
fn rebuild(self, state: &mut Self::State) {
|
||||||
(move || Suspend(async move { self.await })).rebuild(state)
|
(move || Suspend::new(async move { self.await })).rebuild(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +239,7 @@ mod view_implementations {
|
||||||
where
|
where
|
||||||
Self::Output<NewAttr>: RenderHtml<R>,
|
Self::Output<NewAttr>: RenderHtml<R>,
|
||||||
{
|
{
|
||||||
(move || Suspend(async move { self.await })).add_any_attr(attr)
|
(move || Suspend::new(async move { self.await })).add_any_attr(attr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ mod view_implementations {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send {
|
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send {
|
||||||
(move || Suspend(async move { self.await })).resolve()
|
(move || Suspend::new(async move { self.await })).resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_html_with_buf(
|
fn to_html_with_buf(
|
||||||
|
@ -267,7 +267,7 @@ mod view_implementations {
|
||||||
position: &mut Position,
|
position: &mut Position,
|
||||||
escape: bool,
|
escape: bool,
|
||||||
) {
|
) {
|
||||||
(move || Suspend(async move { self.await }))
|
(move || Suspend::new(async move { self.await }))
|
||||||
.to_html_with_buf(buf, position, escape);
|
.to_html_with_buf(buf, position, escape);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ mod view_implementations {
|
||||||
) where
|
) where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
(move || Suspend(async move { self.await }))
|
(move || Suspend::new(async move { self.await }))
|
||||||
.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position, escape);
|
.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position, escape);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +288,7 @@ mod view_implementations {
|
||||||
cursor: &Cursor<R>,
|
cursor: &Cursor<R>,
|
||||||
position: &PositionState,
|
position: &PositionState,
|
||||||
) -> Self::State {
|
) -> Self::State {
|
||||||
(move || Suspend(async move { self.await }))
|
(move || Suspend::new(async move { self.await }))
|
||||||
.hydrate::<FROM_SERVER>(cursor, position)
|
.hydrate::<FROM_SERVER>(cursor, position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,13 @@ use std::{
|
||||||
pin_project! {
|
pin_project! {
|
||||||
/// A [`Future`] wrapper that sets the [`Owner`] and [`Observer`] before polling the inner
|
/// A [`Future`] wrapper that sets the [`Owner`] and [`Observer`] before polling the inner
|
||||||
/// `Future`.
|
/// `Future`.
|
||||||
|
#[derive(Clone)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
pub struct ScopedFuture<Fut> {
|
pub struct ScopedFuture<Fut> {
|
||||||
owner: Option<Owner>,
|
pub owner: Option<Owner>,
|
||||||
observer: Option<AnySubscriber>,
|
pub observer: Option<AnySubscriber>,
|
||||||
#[pin]
|
#[pin]
|
||||||
fut: Fut,
|
pub fut: Fut,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,14 @@ use std::{cell::RefCell, fmt::Debug, future::Future, pin::Pin, rc::Rc};
|
||||||
|
|
||||||
/// A suspended `Future`, which can be used in the view.
|
/// A suspended `Future`, which can be used in the view.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Suspend<Fut>(pub Fut);
|
pub struct Suspend<Fut>(pub ScopedFuture<Fut>); // TODO probably shouldn't be pub
|
||||||
|
|
||||||
|
impl<Fut> Suspend<Fut> {
|
||||||
|
/// Creates a new suspended view.
|
||||||
|
pub fn new(fut: Fut) -> Self {
|
||||||
|
Self(ScopedFuture::new(fut))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Fut> Debug for Suspend<Fut> {
|
impl<Fut> Debug for Suspend<Fut> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
@ -69,7 +76,7 @@ where
|
||||||
// poll the future once immediately
|
// poll the future once immediately
|
||||||
// if it's already available, start in the ready state
|
// if it's already available, start in the ready state
|
||||||
// otherwise, start with the fallback
|
// otherwise, start with the fallback
|
||||||
let mut fut = Box::pin(ScopedFuture::new(self.0));
|
let mut fut = Box::pin(self.0);
|
||||||
let initial = fut.as_mut().now_or_never();
|
let initial = fut.as_mut().now_or_never();
|
||||||
let initially_pending = initial.is_none();
|
let initially_pending = initial.is_none();
|
||||||
let inner = Rc::new(RefCell::new(initial.build()));
|
let inner = Rc::new(RefCell::new(initial.build()));
|
||||||
|
@ -96,7 +103,7 @@ where
|
||||||
|
|
||||||
fn rebuild(self, state: &mut Self::State) {
|
fn rebuild(self, state: &mut Self::State) {
|
||||||
// get a unique ID if there's a SuspenseContext
|
// get a unique ID if there's a SuspenseContext
|
||||||
let fut = ScopedFuture::new(self.0);
|
let fut = self.0;
|
||||||
let id = use_context::<SuspenseContext>().map(|sc| sc.task_id());
|
let id = use_context::<SuspenseContext>().map(|sc| sc.task_id());
|
||||||
|
|
||||||
// spawn the future, and rebuild the state when it resolves
|
// spawn the future, and rebuild the state when it resolves
|
||||||
|
@ -137,10 +144,19 @@ where
|
||||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||||
{
|
{
|
||||||
let attr = attr.into_cloneable_owned();
|
let attr = attr.into_cloneable_owned();
|
||||||
Suspend(Box::pin(async move {
|
let ScopedFuture {
|
||||||
let this = self.0.await;
|
owner,
|
||||||
this.add_any_attr(attr)
|
observer,
|
||||||
}))
|
fut,
|
||||||
|
} = self.0;
|
||||||
|
Suspend(ScopedFuture {
|
||||||
|
owner,
|
||||||
|
observer,
|
||||||
|
fut: Box::pin(async move {
|
||||||
|
let this = fut.await;
|
||||||
|
this.add_any_attr(attr)
|
||||||
|
}),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +250,7 @@ where
|
||||||
// poll the future once immediately
|
// poll the future once immediately
|
||||||
// if it's already available, start in the ready state
|
// if it's already available, start in the ready state
|
||||||
// otherwise, start with the fallback
|
// otherwise, start with the fallback
|
||||||
let mut fut = Box::pin(ScopedFuture::new(self.0));
|
let mut fut = Box::pin(self.0);
|
||||||
let initial = fut.as_mut().now_or_never();
|
let initial = fut.as_mut().now_or_never();
|
||||||
let initially_pending = initial.is_none();
|
let initially_pending = initial.is_none();
|
||||||
let inner = Rc::new(RefCell::new(
|
let inner = Rc::new(RefCell::new(
|
||||||
|
|
Loading…
Reference in New Issue