浏览代码

Fix generational box in release mode

Jonathan Kelley 1 年之前
父节点
当前提交
028f499bf6

+ 1 - 1
examples/counter.rs

@@ -29,7 +29,7 @@ fn app() -> Element {
 }
 
 #[component]
-fn Child(i: usize, counters: Signal<Vec<i32>>) -> Element {
+fn Child(counters: Signal<Vec<i32>>, i: usize) -> Element {
     rsx! {
         li {
             button { onclick: move |_| counters.write()[i] -= 1, "-1" }

+ 9 - 9
examples/crm.rs

@@ -36,21 +36,21 @@ struct Client {
 #[derive(Routable, Clone)]
 enum Route {
     #[route("/")]
-    ClientList {},
+    ClientList,
 
     #[route("/new")]
-    ClientAdd {},
+    ClientAdd,
 
     #[route("/settings")]
-    Settings {},
+    Settings,
 }
 
 #[component]
 fn ClientList() -> Element {
     rsx! {
         h2 { "List of Clients" }
-        Link { to: Route::ClientAdd {}, class: "pure-button pure-button-primary", "Add Client" }
-        Link { to: Route::Settings {}, class: "pure-button", "Settings" }
+        Link { to: Route::ClientAdd, class: "pure-button pure-button-primary", "Add Client" }
+        Link { to: Route::Settings, class: "pure-button", "Settings" }
         for client in CLIENTS.read().iter() {
             div { class: "client", style: "margin-bottom: 50px",
                 p { "Name: {client.first_name} {client.last_name}" }
@@ -75,7 +75,7 @@ fn ClientAdd() -> Element {
         });
 
         // And then navigate back to the client list
-        dioxus::router::router().push(Route::ClientList {});
+        dioxus::router::router().push(Route::ClientList);
     };
 
     rsx! {
@@ -121,7 +121,7 @@ fn ClientAdd() -> Element {
 
                 div { class: "pure-controls",
                     button { r#type: "submit", class: "pure-button pure-button-primary", "Save" }
-                    Link { to: Route::ClientList {}, class: "pure-button pure-button-primary red", "Cancel" }
+                    Link { to: Route::ClientList, class: "pure-button pure-button-primary red", "Cancel" }
                 }
             }
         }
@@ -136,10 +136,10 @@ fn Settings() -> Element {
             class: "pure-button pure-button-primary red",
             onclick: move |_| {
                 CLIENTS.write().clear();
-                dioxus::router::router().push(Route::ClientList {});
+                dioxus::router::router().push(Route::ClientList);
             },
             "Remove all Clients"
         }
-        Link { to: Route::ClientList {}, class: "pure-button", "Go back" }
+        Link { to: Route::ClientList, class: "pure-button", "Go back" }
     }
 }

+ 2 - 2
examples/dog_app.rs

@@ -7,7 +7,7 @@ fn main() {
 
 fn app() -> Element {
     let mut breed = use_signal(|| "deerhound".to_string());
-    let mut breed_list = use_future(|| async move {
+    let breed_list = use_resource(|| async move {
         let list = reqwest::get("https://dog.ceo/api/breeds/list/all")
             .await
             .unwrap()
@@ -44,7 +44,7 @@ fn app() -> Element {
 
 #[component]
 fn BreedPic(breed: Signal<String>) -> Element {
-    let fut = use_future(|| async move {
+    let fut = use_resource(|| async move {
         reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random"))
             .await
             .unwrap()

+ 1 - 1
packages/desktop/src/launch.rs

@@ -30,7 +30,7 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf
                 EventData::Poll => app.poll_vdom(id),
                 EventData::NewWindow => app.handle_new_window(),
                 EventData::CloseWindow => app.handle_close_msg(id),
-                #[cfg(feature = "hot-reload")]
+                #[cfg(all(feature = "hot-reload", debug_assertions))]
                 EventData::HotReloadEvent(msg) => app.handle_hot_reload_msg(msg),
                 EventData::Ipc(msg) => match msg.method() {
                     IpcMethod::FileDialog => app.handle_file_dialog_msg(msg, id),

+ 6 - 1
packages/generational-box/src/lib.rs

@@ -244,6 +244,7 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
         );
 
         if result.is_ok() {
+            #[cfg(any(debug_assertions, feature = "debug_ownership"))]
             self.raw
                 .0
                 .borrow
@@ -280,7 +281,10 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
         );
 
         if result.is_ok() {
-            *self.raw.0.borrow.borrowed_mut_at.write() = Some(std::panic::Location::caller());
+            #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+            {
+                *self.raw.0.borrow.borrowed_mut_at.write() = Some(std::panic::Location::caller());
+            }
         }
 
         result
@@ -419,6 +423,7 @@ struct MemoryLocationBorrowInfo {
     pub(crate) borrowed_mut_at: parking_lot::RwLock<Option<&'static std::panic::Location<'static>>>,
 }
 
+#[cfg(any(debug_assertions, feature = "debug_ownership"))]
 impl MemoryLocationBorrowInfo {
     fn borrow_mut_error(&self) -> BorrowMutError {
         if let Some(borrowed_mut_at) = self.borrowed_mut_at.read().as_ref() {

+ 17 - 8
packages/generational-box/src/sync.rs

@@ -89,10 +89,15 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]
         at: crate::GenerationalRefBorrowInfo,
     ) -> Result<Self::Ref, error::BorrowError> {
-        let read = self
-            .0
-            .try_read()
-            .ok_or_else(|| at.borrowed_from.borrow_error())?;
+        let read = self.0.try_read();
+        // .ok_or_else(|| at.borrowed_from.borrow_error())?;
+
+        #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+        let read = read.ok_or_else(|| at.borrowed_from.borrow_error())?;
+
+        #[cfg(not(any(debug_assertions, feature = "debug_ownership")))]
+        let read = read.unwrap();
+
         RwLockReadGuard::try_map(read, |any| any.as_ref()?.downcast_ref())
             .map_err(|_| {
                 error::BorrowError::Dropped(ValueDroppedError {
@@ -116,10 +121,14 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]
         at: crate::GenerationalRefMutBorrowInfo,
     ) -> Result<Self::Mut, error::BorrowMutError> {
-        let write = self
-            .0
-            .try_write()
-            .ok_or_else(|| at.borrowed_from.borrow_mut_error())?;
+        let write = self.0.try_write();
+
+        #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+        let write = write.ok_or_else(|| at.borrowed_from.borrow_mut_error())?;
+
+        #[cfg(not(any(debug_assertions, feature = "debug_ownership")))]
+        let write = write.unwrap();
+
         RwLockWriteGuard::try_map(write, |any| any.as_mut()?.downcast_mut())
             .map_err(|_| {
                 error::BorrowMutError::Dropped(ValueDroppedError {

+ 16 - 8
packages/generational-box/src/unsync.rs

@@ -54,10 +54,14 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]
         at: crate::GenerationalRefBorrowInfo,
     ) -> Result<Self::Ref, crate::error::BorrowError> {
-        let borrow = self
-            .0
-            .try_borrow()
-            .map_err(|_| at.borrowed_from.borrow_error())?;
+        let borrow = self.0.try_borrow();
+
+        #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+        let borrow = borrow.map_err(|_| at.borrowed_from.borrow_error())?;
+
+        #[cfg(not(any(debug_assertions, feature = "debug_ownership")))]
+        let borrow = borrow.unwrap();
+
         Ref::filter_map(borrow, |any| any.as_ref()?.downcast_ref())
             .map_err(|_| {
                 crate::error::BorrowError::Dropped(crate::error::ValueDroppedError {
@@ -81,10 +85,14 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]
         at: crate::GenerationalRefMutBorrowInfo,
     ) -> Result<Self::Mut, crate::error::BorrowMutError> {
-        let borrow = self
-            .0
-            .try_borrow_mut()
-            .map_err(|_| at.borrowed_from.borrow_mut_error())?;
+        let borrow = self.0.try_borrow_mut();
+
+        #[cfg(any(debug_assertions, feature = "debug_ownership"))]
+        let borrow = borrow.map_err(|_| at.borrowed_from.borrow_mut_error())?;
+
+        #[cfg(not(any(debug_assertions, feature = "debug_ownership")))]
+        let borrow = borrow.unwrap();
+
         RefMut::filter_map(borrow, |any| any.as_mut()?.downcast_mut())
             .map_err(|_| {
                 crate::error::BorrowMutError::Dropped(crate::error::ValueDroppedError {

+ 3 - 0
packages/hooks/src/lib.rs

@@ -70,6 +70,9 @@ pub use use_future::*;
 mod use_sorted;
 pub use use_sorted::*;
 
+mod use_resource;
+pub use use_resource::*;
+
 // mod use_on_create;
 // pub use use_on_create::*;
 

+ 0 - 4
packages/hooks/src/use_future.rs

@@ -15,11 +15,7 @@ use std::{any::Any, cell::Cell, future::Future, pin::Pin, rc::Rc, sync::Arc, tas
 /// This is commonly used for components that cannot be rendered until some
 /// asynchronous operation has completed.
 ///
-/// Whenever the hooks dependencies change, the future will be re-evaluated.
-/// If a future is pending when the dependencies change, the previous future
-/// will be canceled before the new one is started.
 ///
-/// - dependencies: a tuple of references to values that are PartialEq + Clone
 pub fn use_future<T, F>(mut future: impl FnMut() -> F) -> UseFuture<T>
 where
     T: 'static,

+ 127 - 0
packages/hooks/src/use_resource.rs

@@ -0,0 +1,127 @@
+#![allow(missing_docs)]
+use dioxus_core::{
+    prelude::{spawn, use_hook},
+    ScopeState, Task,
+};
+use dioxus_signals::{use_effect, use_signal, Effect, Signal};
+use futures_util::{future, pin_mut, FutureExt};
+use std::{any::Any, cell::Cell, future::Future, pin::Pin, rc::Rc, sync::Arc, task::Poll};
+
+/// A future that resolves to a value.
+///
+/// This runs the future only once - though the future may be regenerated
+/// through the [`UseFuture::restart`] method.
+///
+/// This is commonly used for components that cannot be rendered until some
+/// asynchronous operation has completed.
+///
+/// Whenever the hooks dependencies change, the future will be re-evaluated.
+/// If a future is pending when the dependencies change, the previous future
+/// will be canceled before the new one is started.
+///
+/// - dependencies: a tuple of references to values that are PartialEq + Clone
+pub fn use_resource<T, F>(future: impl Fn() -> F) -> UseResource<T>
+where
+    T: 'static,
+    F: Future<Output = T> + 'static,
+{
+    let mut value = use_signal(|| None);
+    let mut state = use_signal(|| UseResourceState::Pending);
+
+    let task = use_signal(|| {
+        // Create the user's task
+        let fut = future();
+
+        // Spawn a wrapper task that polls the innner future and watch its dependencies
+        let task = spawn(async move {
+            // move the future here and pin it so we can poll it
+            let fut = fut;
+            pin_mut!(fut);
+
+            let res = future::poll_fn(|cx| {
+                // Set the effect stack properly
+
+                // Poll the inner future
+                let ready = fut.poll_unpin(cx);
+
+                // add any dependencies to the effect stack that we need to watch when restarting the future
+
+                ready
+            })
+            .await;
+
+            // Set the value
+            value.set(Some(res));
+        });
+
+        Some(task)
+    });
+
+    UseResource { task, value, state }
+}
+
+pub struct UseResource<T: 'static> {
+    value: Signal<Option<T>>,
+    task: Signal<Option<Task>>,
+    state: Signal<UseResourceState<T>>,
+}
+
+impl<T> UseResource<T> {
+    /// Restart the future with new dependencies.
+    ///
+    /// Will not cancel the previous future, but will ignore any values that it
+    /// generates.
+    pub fn restart(&self) {
+        // self.needs_regen.set(true);
+        // (self.update)();
+    }
+
+    /// Forcefully cancel a future
+    pub fn cancel(&self) {
+        // if let Some(task) = self.task.take() {
+        //     cx.remove_future(task);
+        // }
+    }
+
+    // Manually set the value in the future slot without starting the future over
+    pub fn set(&mut self, new_value: T) {
+        self.value.set(Some(new_value));
+    }
+
+    /// Return any value, even old values if the future has not yet resolved.
+    ///
+    /// If the future has never completed, the returned value will be `None`.
+    pub fn value(&self) -> Signal<Option<T>> {
+        self.value
+    }
+
+    /// Get the ID of the future in Dioxus' internal scheduler
+    pub fn task(&self) -> Option<Task> {
+        todo!()
+        // self.task.get()
+    }
+
+    /// Get the current state of the future.
+    pub fn state(&self) -> UseResourceState<T> {
+        todo!()
+        // match (&self.task.get(), &self.value()) {
+        //     // If we have a task and an existing value, we're reloading
+        //     (Some(_), Some(val)) => UseResourceState::Reloading(val),
+
+        //     // no task, but value - we're done
+        //     (None, Some(val)) => UseResourceState::Complete(val),
+
+        //     // no task, no value - something's wrong? return pending
+        //     (None, None) => UseResourceState::Pending,
+
+        //     // Task, no value - we're still pending
+        //     (Some(_), None) => UseResourceState::Pending,
+        // }
+    }
+}
+
+pub enum UseResourceState<T: 'static> {
+    Pending,
+    Complete(Signal<T>),
+    Regenerating(Signal<T>), // the old value
+}