mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
change: pass Scope
as argument into Resource::read()
and Resource::with()
(#542)
This commit is contained in:
parent
ce4b0ecbe1
commit
4ff08f042b
18 changed files with 80 additions and 97 deletions
|
@ -97,10 +97,10 @@ pub fn Counter(cx: Scope) -> impl IntoView {
|
|||
|_| get_server_count(),
|
||||
);
|
||||
|
||||
let value = move || counter.read().map(|count| count.unwrap_or(0)).unwrap_or(0);
|
||||
let value = move || counter.read(cx).map(|count| count.unwrap_or(0)).unwrap_or(0);
|
||||
let error_msg = move || {
|
||||
counter
|
||||
.read()
|
||||
.read(cx)
|
||||
.map(|res| match res {
|
||||
Ok(_) => None,
|
||||
Err(e) => Some(e),
|
||||
|
@ -143,7 +143,7 @@ pub fn FormCounter(cx: Scope) -> impl IntoView {
|
|||
let value = move || {
|
||||
log::debug!("FormCounter looking for value");
|
||||
counter
|
||||
.read()
|
||||
.read(cx)
|
||||
.map(|n| n.ok())
|
||||
.flatten()
|
||||
.map(|n| n)
|
||||
|
|
|
@ -60,7 +60,7 @@ pub fn fetch_example(cx: Scope) -> impl IntoView {
|
|||
// and by using the ErrorBoundary fallback to catch Err(_)
|
||||
// so we'll just implement our happy path and let the framework handle the rest
|
||||
let cats_view = move || {
|
||||
cats.with(|data| {
|
||||
cats.with(cx, |data| {
|
||||
data.iter()
|
||||
.flatten()
|
||||
.map(|cat| view! { cx, <img src={cat}/> })
|
||||
|
|
|
@ -38,7 +38,7 @@ pub fn Stories(cx: Scope) -> impl IntoView {
|
|||
let (pending, set_pending) = create_signal(cx, false);
|
||||
|
||||
let hide_more_link =
|
||||
move || pending() || stories.read().unwrap_or(None).unwrap_or_default().len() < 28;
|
||||
move || pending() || stories.read(cx).unwrap_or(None).unwrap_or_default().len() < 28;
|
||||
|
||||
view! {
|
||||
cx,
|
||||
|
@ -82,7 +82,7 @@ pub fn Stories(cx: Scope) -> impl IntoView {
|
|||
fallback=move || view! { cx, <p>"Loading..."</p> }
|
||||
set_pending=set_pending.into()
|
||||
>
|
||||
{move || match stories.read() {
|
||||
{move || match stories.read(cx) {
|
||||
None => None,
|
||||
Some(None) => Some(view! { cx, <p>"Error loading stories."</p> }.into_any()),
|
||||
Some(Some(stories)) => {
|
||||
|
|
|
@ -17,13 +17,13 @@ pub fn Story(cx: Scope) -> impl IntoView {
|
|||
}
|
||||
},
|
||||
);
|
||||
let meta_description = move || story.read().and_then(|story| story.map(|story| story.title)).unwrap_or_else(|| "Loading story...".to_string());
|
||||
let meta_description = move || story.read(cx).and_then(|story| story.map(|story| story.title)).unwrap_or_else(|| "Loading story...".to_string());
|
||||
|
||||
view! { cx,
|
||||
<>
|
||||
<Meta name="description" content=meta_description/>
|
||||
<Suspense fallback=|| view! { cx, "Loading..." }>
|
||||
{move || story.read().map(|story| match story {
|
||||
{move || story.read(cx).map(|story| match story {
|
||||
None => view! { cx, <div class="item-view">"Error loading this story."</div> },
|
||||
Some(story) => view! { cx,
|
||||
<div class="item-view">
|
||||
|
|
|
@ -19,7 +19,7 @@ pub fn User(cx: Scope) -> impl IntoView {
|
|||
view! { cx,
|
||||
<div class="user-view">
|
||||
<Suspense fallback=|| view! { cx, "Loading..." }>
|
||||
{move || user.read().map(|user| match user {
|
||||
{move || user.read(cx).map(|user| match user {
|
||||
None => view! { cx, <h1>"User not found."</h1> }.into_any(),
|
||||
Some(user) => view! { cx,
|
||||
<div>
|
||||
|
|
|
@ -38,7 +38,7 @@ pub fn Stories(cx: Scope) -> impl IntoView {
|
|||
let (pending, set_pending) = create_signal(cx, false);
|
||||
|
||||
let hide_more_link =
|
||||
move || pending() || stories.read().unwrap_or(None).unwrap_or_default().len() < 28;
|
||||
move || pending() || stories.read(cx).unwrap_or(None).unwrap_or_default().len() < 28;
|
||||
|
||||
view! {
|
||||
cx,
|
||||
|
@ -82,7 +82,7 @@ pub fn Stories(cx: Scope) -> impl IntoView {
|
|||
fallback=move || view! { cx, <p>"Loading..."</p> }
|
||||
set_pending=set_pending.into()
|
||||
>
|
||||
{move || match stories.read() {
|
||||
{move || match stories.read(cx) {
|
||||
None => None,
|
||||
Some(None) => Some(view! { cx, <p>"Error loading stories."</p> }.into_any()),
|
||||
Some(Some(stories)) => {
|
||||
|
|
|
@ -17,13 +17,13 @@ pub fn Story(cx: Scope) -> impl IntoView {
|
|||
}
|
||||
},
|
||||
);
|
||||
let meta_description = move || story.read().and_then(|story| story.map(|story| story.title)).unwrap_or_else(|| "Loading story...".to_string());
|
||||
let meta_description = move || story.read(cx).and_then(|story| story.map(|story| story.title)).unwrap_or_else(|| "Loading story...".to_string());
|
||||
|
||||
view! { cx,
|
||||
<>
|
||||
<Meta name="description" content=meta_description/>
|
||||
<Suspense fallback=|| view! { cx, "Loading..." }>
|
||||
{move || story.read().map(|story| match story {
|
||||
{move || story.read(cx).map(|story| match story {
|
||||
None => view! { cx, <div class="item-view">"Error loading this story."</div> },
|
||||
Some(story) => view! { cx,
|
||||
<div class="item-view">
|
||||
|
|
|
@ -19,7 +19,7 @@ pub fn User(cx: Scope) -> impl IntoView {
|
|||
view! { cx,
|
||||
<div class="user-view">
|
||||
<Suspense fallback=|| view! { cx, "Loading..." }>
|
||||
{move || user.read().map(|user| match user {
|
||||
{move || user.read(cx).map(|user| match user {
|
||||
None => view! { cx, <h1>"User not found."</h1> }.into_any(),
|
||||
Some(user) => view! { cx,
|
||||
<div>
|
||||
|
|
|
@ -71,9 +71,10 @@ pub fn ContactList(cx: Scope) -> impl IntoView {
|
|||
});
|
||||
|
||||
let location = use_location(cx);
|
||||
let contacts = create_resource(cx, move || location.search.get(), get_contacts);
|
||||
let contacts =
|
||||
create_resource(cx, move || location.search.get(), get_contacts);
|
||||
let contacts = move || {
|
||||
contacts.read().map(|contacts| {
|
||||
contacts.read(cx).map(|contacts| {
|
||||
// this data doesn't change frequently so we can use .map().collect() instead of a keyed <For/>
|
||||
contacts
|
||||
.into_iter()
|
||||
|
@ -126,12 +127,15 @@ pub fn Contact(cx: Scope) -> impl IntoView {
|
|||
get_contact,
|
||||
);
|
||||
|
||||
let contact_display = move || match contact.read() {
|
||||
let contact_display = move || match contact.read(cx) {
|
||||
// None => loading, but will be caught by Suspense fallback
|
||||
// I'm only doing this explicitly for the example
|
||||
None => None,
|
||||
// Some(None) => has loaded and found no contact
|
||||
Some(None) => Some(view! { cx, <p>"No contact with this ID was found."</p> }.into_any()),
|
||||
Some(None) => Some(
|
||||
view! { cx, <p>"No contact with this ID was found."</p> }
|
||||
.into_any(),
|
||||
),
|
||||
// Some(Some) => has loaded and found a contact
|
||||
Some(Some(contact)) => Some(
|
||||
view! { cx,
|
||||
|
|
|
@ -39,7 +39,7 @@ fn HomePage(cx: Scope) -> impl IntoView {
|
|||
let posts =
|
||||
create_resource(cx, || (), |_| async { list_post_metadata().await });
|
||||
let posts_view = move || {
|
||||
posts.with(|posts| posts
|
||||
posts.with(cx, |posts| posts
|
||||
.clone()
|
||||
.map(|posts| {
|
||||
posts.iter()
|
||||
|
@ -82,7 +82,7 @@ fn Post(cx: Scope) -> impl IntoView {
|
|||
});
|
||||
|
||||
let post_view = move || {
|
||||
post.with(|post| {
|
||||
post.with(cx, |post| {
|
||||
post.clone().map(|post| {
|
||||
view! { cx,
|
||||
// render content
|
||||
|
|
|
@ -140,7 +140,7 @@ pub fn Todos(cx: Scope) -> impl IntoView {
|
|||
{move || {
|
||||
let existing_todos = {
|
||||
move || {
|
||||
todos.read()
|
||||
todos.read(cx)
|
||||
.map(move |todos| match todos {
|
||||
Err(e) => {
|
||||
vec![view! { cx, <pre class="error">"Server Error: " {e.to_string()}</pre>}.into_any()]
|
||||
|
|
|
@ -159,7 +159,7 @@ pub fn Todos(cx: Scope) -> impl IntoView {
|
|||
{move || {
|
||||
let existing_todos = {
|
||||
move || {
|
||||
todos.read()
|
||||
todos.read(cx)
|
||||
.map(move |todos| match todos {
|
||||
Err(e) => {
|
||||
vec![view! { cx, <pre class="error">"Server Error: " {e.to_string()}</pre>}.into_any()]
|
||||
|
|
|
@ -28,7 +28,7 @@ use std::rc::Rc;
|
|||
/// <div>
|
||||
/// <Suspense fallback=move || view! { cx, <p>"Loading (Suspense Fallback)..."</p> }>
|
||||
/// {move || {
|
||||
/// cats.read().map(|data| match data {
|
||||
/// cats.read(cx).map(|data| match data {
|
||||
/// None => view! { cx, <pre>"Error"</pre> }.into_any(),
|
||||
/// Some(cats) => view! { cx,
|
||||
/// <div>{
|
||||
|
|
|
@ -35,7 +35,7 @@ use std::{cell::RefCell, rc::Rc};
|
|||
/// set_pending=set_pending.into()
|
||||
/// >
|
||||
/// {move || {
|
||||
/// cats.read().map(|data| match data {
|
||||
/// cats.read(cx).map(|data| match data {
|
||||
/// None => view! { cx, <pre>"Error"</pre> }.into_any(),
|
||||
/// Some(cats) => view! { cx,
|
||||
/// <div>{
|
||||
|
|
|
@ -54,11 +54,11 @@ use std::{
|
|||
/// // when we read the signal, it contains either
|
||||
/// // 1) None (if the Future isn't ready yet) or
|
||||
/// // 2) Some(T) (if the future's already resolved)
|
||||
/// assert_eq!(cats(), Some(vec!["1".to_string()]));
|
||||
/// assert_eq!(cats.read(cx), Some(vec!["1".to_string()]));
|
||||
///
|
||||
/// // when the signal's value changes, the `Resource` will generate and run a new `Future`
|
||||
/// set_how_many_cats(2);
|
||||
/// assert_eq!(cats(), Some(vec!["2".to_string()]));
|
||||
/// assert_eq!(cats.read(cx), Some(vec!["2".to_string()]));
|
||||
/// # }
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
|
@ -121,7 +121,6 @@ where
|
|||
let source = create_memo(cx, move |_| source());
|
||||
|
||||
let r = Rc::new(ResourceState {
|
||||
scope: cx,
|
||||
value,
|
||||
set_value,
|
||||
loading,
|
||||
|
@ -245,7 +244,6 @@ where
|
|||
let source = create_memo(cx, move |_| source());
|
||||
|
||||
let r = Rc::new(ResourceState {
|
||||
scope: cx,
|
||||
value,
|
||||
set_value,
|
||||
loading,
|
||||
|
@ -371,14 +369,14 @@ where
|
|||
/// resource.
|
||||
///
|
||||
/// If you want to get the value without cloning it, use [Resource::with].
|
||||
/// (`value.read()` is equivalent to `value.with(T::clone)`.)
|
||||
pub fn read(&self) -> Option<T>
|
||||
/// (`value.read(cx)` is equivalent to `value.with(cx, T::clone)`.)
|
||||
pub fn read(&self, cx: Scope) -> Option<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
with_runtime(self.runtime, |runtime| {
|
||||
runtime.resource(self.id, |resource: &ResourceState<S, T>| {
|
||||
resource.read()
|
||||
resource.read(cx)
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
|
@ -392,10 +390,10 @@ where
|
|||
///
|
||||
/// If you want to get the value by cloning it, you can use
|
||||
/// [Resource::read].
|
||||
pub fn with<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U> {
|
||||
pub fn with<U>(&self, cx: Scope, f: impl FnOnce(&T) -> U) -> Option<U> {
|
||||
with_runtime(self.runtime, |runtime| {
|
||||
runtime.resource(self.id, |resource: &ResourceState<S, T>| {
|
||||
resource.with(f)
|
||||
resource.with(cx, f)
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
|
@ -427,13 +425,16 @@ where
|
|||
/// Returns a [std::future::Future] that will resolve when the resource has loaded,
|
||||
/// yield its [ResourceId] and a JSON string.
|
||||
#[cfg(any(feature = "ssr", doc))]
|
||||
pub async fn to_serialization_resolver(&self) -> (ResourceId, String)
|
||||
pub async fn to_serialization_resolver(
|
||||
&self,
|
||||
cx: Scope,
|
||||
) -> (ResourceId, String)
|
||||
where
|
||||
T: Serializable,
|
||||
{
|
||||
with_runtime(self.runtime, |runtime| {
|
||||
runtime.resource(self.id, |resource: &ResourceState<S, T>| {
|
||||
resource.to_serialization_resolver(self.id)
|
||||
resource.to_serialization_resolver(cx, self.id)
|
||||
})
|
||||
})
|
||||
.expect(
|
||||
|
@ -479,11 +480,11 @@ where
|
|||
/// // when we read the signal, it contains either
|
||||
/// // 1) None (if the Future isn't ready yet) or
|
||||
/// // 2) Some(T) (if the future's already resolved)
|
||||
/// assert_eq!(cats(), Some(vec!["1".to_string()]));
|
||||
/// assert_eq!(cats.read(cx), Some(vec!["1".to_string()]));
|
||||
///
|
||||
/// // when the signal's value changes, the `Resource` will generate and run a new `Future`
|
||||
/// set_how_many_cats(2);
|
||||
/// assert_eq!(cats(), Some(vec!["2".to_string()]));
|
||||
/// assert_eq!(cats.read(cx), Some(vec!["2".to_string()]));
|
||||
/// # }
|
||||
/// # }).dispose();
|
||||
/// ```
|
||||
|
@ -531,48 +532,12 @@ where
|
|||
{
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl<S, T> FnOnce<()> for Resource<S, T>
|
||||
where
|
||||
S: Clone + 'static,
|
||||
T: Clone + 'static,
|
||||
{
|
||||
type Output = Option<T>;
|
||||
|
||||
extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
|
||||
self.read()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl<S, T> FnMut<()> for Resource<S, T>
|
||||
where
|
||||
S: Clone + 'static,
|
||||
T: Clone + 'static,
|
||||
{
|
||||
extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
|
||||
self.read()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "stable"))]
|
||||
impl<S, T> Fn<()> for Resource<S, T>
|
||||
where
|
||||
S: Clone + 'static,
|
||||
T: Clone + 'static,
|
||||
{
|
||||
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
|
||||
self.read()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ResourceState<S, T>
|
||||
where
|
||||
S: 'static,
|
||||
T: 'static,
|
||||
{
|
||||
scope: Scope,
|
||||
value: ReadSignal<Option<T>>,
|
||||
set_value: WriteSignal<Option<T>>,
|
||||
pub loading: ReadSignal<bool>,
|
||||
|
@ -590,15 +555,15 @@ where
|
|||
S: Clone + 'static,
|
||||
T: 'static,
|
||||
{
|
||||
pub fn read(&self) -> Option<T>
|
||||
pub fn read(&self, cx: Scope) -> Option<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
self.with(T::clone)
|
||||
self.with(cx, T::clone)
|
||||
}
|
||||
|
||||
pub fn with<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U> {
|
||||
let suspense_cx = use_context::<SuspenseContext>(self.scope);
|
||||
pub fn with<U>(&self, cx: Scope, f: impl FnOnce(&T) -> U) -> Option<U> {
|
||||
let suspense_cx = use_context::<SuspenseContext>(cx);
|
||||
|
||||
let v = self
|
||||
.value
|
||||
|
@ -611,21 +576,23 @@ where
|
|||
|
||||
let increment = move |_: Option<()>| {
|
||||
if let Some(s) = &suspense_cx {
|
||||
let mut contexts = suspense_contexts.borrow_mut();
|
||||
if !contexts.contains(s) {
|
||||
contexts.insert(*s);
|
||||
if let Ok(ref mut contexts) = suspense_contexts.try_borrow_mut()
|
||||
{
|
||||
if !contexts.contains(s) {
|
||||
contexts.insert(*s);
|
||||
|
||||
// on subsequent reads, increment will be triggered in load()
|
||||
// because the context has been tracked here
|
||||
// on the first read, resource is already loading without having incremented
|
||||
if !has_value {
|
||||
s.increment();
|
||||
// on subsequent reads, increment will be triggered in load()
|
||||
// because the context has been tracked here
|
||||
// on the first read, resource is already loading without having incremented
|
||||
if !has_value {
|
||||
s.increment();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
create_isomorphic_effect(self.scope, increment);
|
||||
create_isomorphic_effect(cx, increment);
|
||||
v
|
||||
}
|
||||
|
||||
|
@ -685,6 +652,7 @@ where
|
|||
|
||||
pub fn resource_to_serialization_resolver(
|
||||
&self,
|
||||
cx: Scope,
|
||||
id: ResourceId,
|
||||
) -> std::pin::Pin<Box<dyn futures::Future<Output = (ResourceId, String)>>>
|
||||
where
|
||||
|
@ -694,7 +662,7 @@ where
|
|||
|
||||
let (tx, mut rx) = futures::channel::mpsc::channel(1);
|
||||
let value = self.value;
|
||||
create_isomorphic_effect(self.scope, move |_| {
|
||||
create_isomorphic_effect(cx, move |_| {
|
||||
value.with({
|
||||
let mut tx = tx.clone();
|
||||
move |value| {
|
||||
|
@ -731,6 +699,7 @@ pub(crate) trait SerializableResource {
|
|||
|
||||
fn to_serialization_resolver(
|
||||
&self,
|
||||
cx: Scope,
|
||||
id: ResourceId,
|
||||
) -> Pin<Box<dyn Future<Output = (ResourceId, String)>>>;
|
||||
}
|
||||
|
@ -746,9 +715,10 @@ where
|
|||
|
||||
fn to_serialization_resolver(
|
||||
&self,
|
||||
cx: Scope,
|
||||
id: ResourceId,
|
||||
) -> Pin<Box<dyn Future<Output = (ResourceId, String)>>> {
|
||||
let fut = self.resource_to_serialization_resolver(id);
|
||||
let fut = self.resource_to_serialization_resolver(cx, id);
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -421,11 +421,12 @@ impl Runtime {
|
|||
|
||||
pub(crate) fn serialization_resolvers(
|
||||
&self,
|
||||
cx: Scope,
|
||||
) -> FuturesUnordered<PinnedFuture<(ResourceId, String)>> {
|
||||
let f = FuturesUnordered::new();
|
||||
for (id, resource) in self.resources.borrow().iter() {
|
||||
if let AnyResource::Serializable(resource) = resource {
|
||||
f.push(resource.to_serialization_resolver(id));
|
||||
f.push(resource.to_serialization_resolver(cx, id));
|
||||
}
|
||||
}
|
||||
f
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![forbid(unsafe_code)]
|
||||
use crate::{
|
||||
console_warn,
|
||||
runtime::{with_runtime, RuntimeId},
|
||||
suspense::StreamChunk,
|
||||
EffectId, PinnedFuture, ResourceId, SignalId, SuspenseContext,
|
||||
|
@ -266,10 +267,13 @@ impl Scope {
|
|||
) {
|
||||
_ = with_runtime(self.runtime, |runtime| {
|
||||
let scopes = runtime.scopes.borrow();
|
||||
let scope = scopes.get(self.id).expect(
|
||||
"tried to add property to a scope that has been disposed",
|
||||
);
|
||||
f(&mut scope.borrow_mut());
|
||||
if let Some(scope) = scopes.get(self.id) {
|
||||
f(&mut scope.borrow_mut());
|
||||
} else {
|
||||
console_warn(
|
||||
"tried to add property to a scope that has been disposed",
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -353,8 +357,10 @@ impl Scope {
|
|||
pub fn serialization_resolvers(
|
||||
&self,
|
||||
) -> FuturesUnordered<PinnedFuture<(ResourceId, String)>> {
|
||||
with_runtime(self.runtime, |runtime| runtime.serialization_resolvers())
|
||||
.unwrap_or_default()
|
||||
with_runtime(self.runtime, |runtime| {
|
||||
runtime.serialization_resolvers(*self)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Registers the given [SuspenseContext](crate::SuspenseContext) with the current scope,
|
||||
|
@ -409,7 +415,9 @@ impl Scope {
|
|||
{
|
||||
with_runtime(self.runtime, |runtime| {
|
||||
let mut shared_context = runtime.shared_context.borrow_mut();
|
||||
std::mem::take(&mut shared_context.pending_fragments)
|
||||
let f = std::mem::take(&mut shared_context.pending_fragments);
|
||||
println!("pending_fragments = {}", f.len());
|
||||
f
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
//! <div>
|
||||
//! // show the contacts
|
||||
//! <ul>
|
||||
//! {move || contacts.read().map(|contacts| view! { cx, <li>"todo contact info"</li> } )}
|
||||
//! {move || contacts.read(cx).map(|contacts| view! { cx, <li>"todo contact info"</li> } )}
|
||||
//! </ul>
|
||||
//!
|
||||
//! // insert the nested child route here
|
||||
|
|
Loading…
Reference in a new issue