Skip to content

Commit 8b2c710

Browse files
committed
Add cloned_async and peek_async
1 parent a13ac07 commit 8b2c710

File tree

1 file changed

+107
-1
lines changed

1 file changed

+107
-1
lines changed

packages/hooks/src/use_resource.rs

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,8 @@ impl<T> Resource<T> {
475475
/// let guard1 = resource1.read_async().await;
476476
/// drop(guard1);
477477
/// let guard2 = resource2.read_async().await;
478-
/// // Value exists
478+
/// // Value exists if used inside another `use_resource`,
479+
/// // since otherwise the resource would have restarted
479480
/// let guard1 = resource1.read().as_ref().unwrap();
480481
/// ```
481482
///
@@ -521,6 +522,111 @@ impl<T> Resource<T> {
521522
}
522523
read.map(|e| std::cell::Ref::map(e, |option| option.as_ref().unwrap()))
523524
}
525+
526+
/// Asynchronously wait for the resource to be ready and return a clone of its value.
527+
///
528+
/// The primary advantage of `cloned_async` is that it avoids the complex
529+
/// borrowing rules of `read_async` by immediately cloning the value, allowing
530+
/// it to be used freely across further `.await` points.
531+
///
532+
/// **This method requires `T` to implement `Clone`.**
533+
///
534+
/// ## Example
535+
///
536+
/// ```rust,no_run
537+
/// # use dioxus::prelude::*;
538+
/// #[derive(Clone, Debug)]
539+
/// struct User { id: u32, name: String }
540+
///
541+
/// fn App() -> Element {
542+
/// let user_id = use_signal(|| 42);
543+
/// let user_resource = use_resource(move || async move {
544+
/// // Some expensive fetch that returns a User
545+
/// fetch_user(*user_id.read()).await
546+
/// });
547+
///
548+
/// let cloned_user = use_resource(move || async move {
549+
/// // Wait for user_resource, clone the User struct, and then continue
550+
/// let user: User = user_resource.cloned_async().await;
551+
///
552+
/// // Safe to use 'user' across async boundaries
553+
/// log_user_activity(user.id).await;
554+
///
555+
/// // Return the cloned value for this resource
556+
/// user
557+
/// });
558+
///
559+
/// rsx! {
560+
/// "Fetched User: {cloned_user:?}"
561+
/// }
562+
/// }
563+
/// # async fn fetch_user(_id: u32) -> User { User { id: 42, name: "Alice".to_string() } }
564+
/// # async fn log_user_activity(_id: u32) {}
565+
/// ```
566+
pub async fn cloned_async<'a>(&'a self) -> T
567+
where
568+
T: Clone,
569+
{
570+
let mut read: generational_box::GenerationalRef<std::cell::Ref<'a, Option<T>>> =
571+
self.read();
572+
while read.is_none() {
573+
drop(read);
574+
let _: () = (*self).await;
575+
read = self.read();
576+
}
577+
read.as_ref().unwrap().clone()
578+
}
579+
580+
/// Asynchronously wait for the resource to be ready and return a guard to its value, *without* subscribing the current component.
581+
///
582+
/// This method is identical to `read_async`, but uses `peek()` internally instead of `read()`.
583+
/// This means the component rendering this code **will not** be re-rendered when the resource value changes.
584+
///
585+
/// ## Important: Handling Guards Across Await Points
586+
///
587+
/// Like `read_async`, **never hold the returned guard across await points.**
588+
/// You must drop the guard or clone the data before awaiting.
589+
///
590+
/// ## Example
591+
///
592+
/// Reading a prerequisite resource without causing a re-render:
593+
///
594+
/// ```rust,no_run
595+
/// # use dioxus::prelude::*;
596+
/// #[derive(Clone, Debug)]
597+
/// struct Config { version: String }
598+
///
599+
/// fn App() -> Element {
600+
/// let config_resource = use_resource(|| async { fetch_config().await });
601+
///
602+
/// let final_data = use_resource(move || async move {
603+
/// // Use peek_async to wait for the config, but not subscribe this
604+
/// // resource's internal future to config_resource's changes.
605+
/// let config_guard = config_resource.peek_async().await;
606+
/// let version = config_guard.version.clone();
607+
/// drop(config_guard); // Drop guard
608+
///
609+
/// // Now safe to proceed
610+
/// fetch_data_for_version(&version).await
611+
/// });
612+
///
613+
/// rsx! { "Data: {final_data:?}" }
614+
/// }
615+
/// # async fn fetch_config() -> Config { Config { version: "v1".to_string() } }
616+
/// # async fn fetch_data_for_version(_v: &str) -> String { "Some Data".to_string() }
617+
/// ```
618+
pub async fn peek_async<'a>(
619+
&'a self,
620+
) -> generational_box::GenerationalRef<std::cell::Ref<'a, T>> {
621+
let mut peek: generational_box::GenerationalRef<std::cell::Ref<'a, Option<T>>> =
622+
self.peek();
623+
while peek.is_none() {
624+
drop(peek);
625+
let _: () = (*self).await;
626+
peek = self.peek();
627+
}
628+
peek.map(|e| std::cell::Ref::map(e, |option| option.as_ref().unwrap()))
629+
}
524630
}
525631

526632
impl<T, E> Resource<Result<T, E>> {

0 commit comments

Comments
 (0)