mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
fix: ensure that LocalResource never creates a SendWrapper on the server
This commit is contained in:
parent
33aa676854
commit
dc2314d5e2
5 changed files with 65 additions and 35 deletions
|
@ -20,6 +20,7 @@ futures = "0.3.30"
|
|||
|
||||
any_spawner = { workspace = true }
|
||||
tachys = { workspace = true, optional = true, features = ["reactive_graph"] }
|
||||
send_wrapper = "0.6"
|
||||
|
||||
# serialization formats
|
||||
serde = { version = "1.0" }
|
||||
|
|
|
@ -7,10 +7,11 @@ use reactive_graph::{
|
|||
AnySource, AnySubscriber, ReactiveNode, Source, Subscriber,
|
||||
ToAnySource, ToAnySubscriber,
|
||||
},
|
||||
owner::{use_context, LocalStorage},
|
||||
owner::use_context,
|
||||
signal::guards::{AsyncPlain, ReadGuard},
|
||||
traits::{DefinedAt, ReadUntracked},
|
||||
};
|
||||
use send_wrapper::SendWrapper;
|
||||
use std::{
|
||||
future::{pending, Future, IntoFuture},
|
||||
panic::Location,
|
||||
|
@ -175,7 +176,7 @@ impl<T> Subscriber for ArcLocalResource<T> {
|
|||
}
|
||||
|
||||
pub struct LocalResource<T> {
|
||||
data: AsyncDerived<T, LocalStorage>,
|
||||
data: AsyncDerived<SendWrapper<T>>,
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
}
|
||||
|
@ -217,9 +218,13 @@ impl<T> LocalResource<T> {
|
|||
|
||||
Self {
|
||||
data: if cfg!(feature = "ssr") {
|
||||
AsyncDerived::new_mock_unsync(fetcher)
|
||||
AsyncDerived::new_mock(fetcher)
|
||||
} else {
|
||||
AsyncDerived::new_unsync(fetcher)
|
||||
let fetcher = SendWrapper::new(fetcher);
|
||||
AsyncDerived::new(move || {
|
||||
let fut = fetcher();
|
||||
async move { SendWrapper::new(fut.await) }
|
||||
})
|
||||
},
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
|
@ -232,9 +237,14 @@ where
|
|||
T: Clone + 'static,
|
||||
{
|
||||
type Output = T;
|
||||
type IntoFuture = AsyncDerivedFuture<T>;
|
||||
type IntoFuture = futures::future::Map<
|
||||
AsyncDerivedFuture<SendWrapper<T>>,
|
||||
fn(SendWrapper<T>) -> T,
|
||||
>;
|
||||
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
use futures::FutureExt;
|
||||
|
||||
if let Some(mut notifier) = use_context::<LocalResourceNotifier>() {
|
||||
notifier.notify();
|
||||
} else if cfg!(feature = "ssr") {
|
||||
|
@ -244,7 +254,7 @@ where
|
|||
always pending on the server."
|
||||
);
|
||||
}
|
||||
self.data.into_future()
|
||||
self.data.into_future().map(|value| (*value).clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,7 +275,8 @@ impl<T> ReadUntracked for LocalResource<T>
|
|||
where
|
||||
T: Send + Sync + 'static,
|
||||
{
|
||||
type Value = ReadGuard<Option<T>, AsyncPlain<Option<T>>>;
|
||||
type Value =
|
||||
ReadGuard<Option<SendWrapper<T>>, AsyncPlain<Option<SendWrapper<T>>>>;
|
||||
|
||||
fn try_read_untracked(&self) -> Option<Self::Value> {
|
||||
if let Some(mut notifier) = use_context::<LocalResourceNotifier>() {
|
||||
|
|
|
@ -21,6 +21,7 @@ use async_lock::RwLock as AsyncRwLock;
|
|||
use core::fmt::Debug;
|
||||
use futures::{channel::oneshot, FutureExt, StreamExt};
|
||||
use or_poisoned::OrPoisoned;
|
||||
use send_wrapper::SendWrapper;
|
||||
use std::{
|
||||
future::Future,
|
||||
mem,
|
||||
|
@ -458,19 +459,6 @@ impl<T: 'static> ArcAsyncDerived<T> {
|
|||
this
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn new_mock_unsync<Fut>(fun: impl Fn() -> Fut + 'static) -> Self
|
||||
where
|
||||
T: 'static,
|
||||
Fut: Future<Output = T> + 'static,
|
||||
{
|
||||
let initial = None::<T>;
|
||||
let (this, _) =
|
||||
spawn_derived!(Executor::spawn_local, initial, fun, false, false);
|
||||
this
|
||||
}
|
||||
|
||||
/// Returns a `Future` that is ready when this resource has next finished loading.
|
||||
pub fn ready(&self) -> AsyncDerivedReadyFuture {
|
||||
AsyncDerivedReadyFuture {
|
||||
|
@ -481,6 +469,28 @@ impl<T: 'static> ArcAsyncDerived<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> ArcAsyncDerived<SendWrapper<T>> {
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn new_mock<Fut>(fun: impl Fn() -> Fut + 'static) -> Self
|
||||
where
|
||||
T: 'static,
|
||||
Fut: Future<Output = T> + 'static,
|
||||
{
|
||||
let initial = None::<SendWrapper<T>>;
|
||||
let fun = move || {
|
||||
let fut = fun();
|
||||
async move {
|
||||
let value = fut.await;
|
||||
SendWrapper::new(value)
|
||||
}
|
||||
};
|
||||
let (this, _) =
|
||||
spawn_derived!(Executor::spawn_local, initial, fun, false, false);
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> ReadUntracked for ArcAsyncDerived<T> {
|
||||
type Value = ReadGuard<Option<T>, AsyncPlain<Option<T>>>;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::{
|
|||
unwrap_signal,
|
||||
};
|
||||
use core::fmt::Debug;
|
||||
use send_wrapper::SendWrapper;
|
||||
use std::{future::Future, ops::DerefMut, panic::Location};
|
||||
|
||||
/// A reactive value that is derived by running an asynchronous computation in response to changes
|
||||
|
@ -164,6 +165,23 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> AsyncDerived<SendWrapper<T>> {
|
||||
#[doc(hidden)]
|
||||
pub fn new_mock<Fut>(fun: impl Fn() -> Fut + 'static) -> Self
|
||||
where
|
||||
T: 'static,
|
||||
Fut: Future<Output = T> + 'static,
|
||||
{
|
||||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new_with_storage(ArcAsyncDerived::new_mock(
|
||||
fun,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsyncDerived<T, LocalStorage>
|
||||
where
|
||||
T: 'static,
|
||||
|
@ -207,21 +225,6 @@ where
|
|||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn new_mock_unsync<Fut>(fun: impl Fn() -> Fut + 'static) -> Self
|
||||
where
|
||||
T: 'static,
|
||||
Fut: Future<Output = T> + 'static,
|
||||
{
|
||||
Self {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: Location::caller(),
|
||||
inner: StoredValue::new_with_storage(
|
||||
ArcAsyncDerived::new_mock_unsync(fun),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> AsyncDerived<T, S>
|
||||
|
|
|
@ -31,6 +31,11 @@ impl<T, Inner> ReadGuard<T, Inner> {
|
|||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the inner guard type.
|
||||
pub fn into_inner(self) -> Inner {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Inner> Clone for ReadGuard<T, Inner>
|
||||
|
|
Loading…
Reference in a new issue