diff --git a/examples/eval.rs b/examples/eval.rs index 736c8d652..a73fb94ab 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -5,7 +5,7 @@ fn main() { } fn app() -> Element { - let future = use_future(|| async move { + let future = use_resource(|| async move { let mut eval = eval( r#" dioxus.send("Hi from JS!"); diff --git a/examples/signals.rs b/examples/signals.rs index 1249c6f0a..811c2f14f 100644 --- a/examples/signals.rs +++ b/examples/signals.rs @@ -27,7 +27,7 @@ fn app() -> Element { button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } button { onclick: move |_| running.toggle(), "Toggle counter" } - button { onclick: move |_| saved_values.push(count.cloned().to_string()), "Save this value" } + button { onclick: move |_| saved_values.push(count().to_string()), "Save this value" } button { onclick: move |_| saved_values.write().clear(), "Clear saved values" } // We can do boolean operations on the current signal value diff --git a/examples/simple_desktop.rs b/examples/simple_desktop.rs deleted file mode 100644 index e683621cf..000000000 --- a/examples/simple_desktop.rs +++ /dev/null @@ -1,99 +0,0 @@ -#![allow(non_snake_case)] - -use dioxus::prelude::*; -use dioxus::router::prelude::*; - -fn main() { - launch_desktop(|| { - rsx! { - Router:: {} - } - }); -} - -#[derive(Routable, Clone)] -#[rustfmt::skip] -enum Route { - #[layout(NavBar)] - #[route("/")] - Home {}, - #[nest("/new")] - #[route("/")] - BlogList {}, - #[route("/:post")] - BlogPost { - post: String, - }, - #[end_nest] - #[route("/oranges")] - Oranges {}, -} - -#[component] -fn NavBar() -> Element { - rsx! { - h1 { "Your app here" } - ul { - li { - Link { to: Route::Home {}, "home" } - } - li { - Link { to: Route::BlogList {}, "blog" } - } - li { - Link { - to: Route::BlogPost { - post: "tim".into(), - }, - "tims' blog" - } - } - li { - Link { - to: Route::BlogPost { - post: "bill".into(), - }, - "bills' blog" - } - } - li { - Link { - to: Route::BlogPost { - post: "james".into(), - }, - "james amazing' blog" - } - } - } - Outlet:: {} - } -} - -#[component] -fn Home() -> Element { - println!("rendering home {:?}", current_scope_id()); - rsx! { h1 { "Home" } } -} - -#[component] -fn BlogList() -> Element { - println!("rendering blog list {:?}", current_scope_id()); - rsx! { div { "Blog List" } } -} - -#[component] -fn BlogPost(post: String) -> Element { - println!("rendering blog post {}", post); - - rsx! { - div { - h3 { "blog post: {post}" } - Link { to: Route::BlogList {}, "back to blog list" } - } - } -} - -#[component] -fn Oranges() -> Element { - rsx!("Oranges are not apples!") -} diff --git a/examples/svg_basic.rs b/examples/svg_basic.rs deleted file mode 100644 index eeb261299..000000000 --- a/examples/svg_basic.rs +++ /dev/null @@ -1,83 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - launch(app); -} - -fn app() -> Element { - rsx! { - svg { - width: "200", - height: "250", - xmlns: "http://www.w3.org/2000/svg", - version: "1.1", - rect { - x: "10", - y: "10", - width: "30", - height: "30", - stroke: "black", - fill: "transparent", - stroke_width: "5", - } - rect { - x: "60", - y: "10", - width: "30", - height: "30", - stroke: "black", - fill: "transparent", - stroke_width: "5", - } - circle { - cx: "25", - cy: "75", - r: "20", - stroke: "red", - fill: "transparent", - stroke_width: "5", - } - ellipse { - cx: "75", - cy: "75", - rx: "20", - ry: "5", - stroke: "red", - fill: "transparent", - stroke_width: "5", - } - line { - x1: "10", - x2: "50", - y1: "110", - y2: "150", - stroke: "orange", - stroke_width: "5", - } - polyline { - points: "60 110 65 120 70 115 75 130 80 125 85 140 90 135 95 150 100 145", - stroke: "orange", - fill: "transparent", - stroke_width: "5", - } - polygon { - points: "50 160 55 180 70 180 60 190 65 205 50 195 35 205 40 190 30 180 45 180", - stroke: "green", - fill: "transparent", - stroke_width: "5", - } - path { - d: "M20,230 Q40,205 50,230 T90,230", - fill: "none", - stroke: "blue", - stroke_width: "5", - } - path { - d: "M9.00001 9C9 62 103.5 124 103.5 178", - stroke: "#3CC4DC", - stroke_linecap: "square", - stroke_width: "5", - } - } - } -} diff --git a/packages/core/src/global_context.rs b/packages/core/src/global_context.rs index c18b47429..aae650135 100644 --- a/packages/core/src/global_context.rs +++ b/packages/core/src/global_context.rs @@ -132,3 +132,100 @@ pub fn schedule_update() -> Arc { pub fn schedule_update_any() -> Arc { Runtime::with_current_scope(|cx| cx.schedule_update_any()).expect("to be in a dioxus runtime") } + +/// Creates a callback that will be run before the component is removed. +/// This can be used to clean up side effects from the component +/// (created with [`use_effect`](crate::use_effect)). +/// +/// Example: +/// ```rust +/// use dioxus::prelude::*; +/// +/// fn app() -> Element { +/// let state = use_signal(|| true); +/// rsx! { +/// for _ in 0..100 { +/// h1 { +/// "spacer" +/// } +/// } +/// if **state { +/// rsx! { +/// child_component {} +/// } +/// } +/// button { +/// onclick: move |_| { +/// state.set(!*state.get()); +/// }, +/// "Unmount element" +/// } +/// } +/// } +/// +/// fn child_component() -> Element { +/// let original_scroll_position = use_signal(|| 0.0); +/// use_effect((), move |_| { +/// to_owned![original_scroll_position]; +/// async move { +/// let window = web_sys::window().unwrap(); +/// let document = window.document().unwrap(); +/// let element = document.get_element_by_id("my_element").unwrap(); +/// element.scroll_into_view(); +/// original_scroll_position.set(window.scroll_y().unwrap()); +/// } +/// }); +/// +/// use_on_destroy({ +/// to_owned![original_scroll_position]; +/// /// restore scroll to the top of the page +/// move || { +/// let window = web_sys::window().unwrap(); +/// window.scroll_with_x_and_y(*original_scroll_position.current(), 0.0); +/// } +/// }); +/// +/// rsx!{ +/// div { +/// id: "my_element", +/// "hello" +/// } +/// } +/// } +/// ``` +pub fn use_drop(destroy: D) { + struct LifeCycle { + /// Wrap the closure in an option so that we can take it out on drop. + ondestroy: Option, + } + + /// On drop, we want to run the closure. + impl Drop for LifeCycle { + fn drop(&mut self) { + if let Some(f) = self.ondestroy.take() { + f(); + } + } + } + + // We need to impl clone for the lifecycle, but we don't want the drop handler for the closure to be called twice. + impl Clone for LifeCycle { + fn clone(&self) -> Self { + Self { ondestroy: None } + } + } + + use_hook(|| LifeCycle { + ondestroy: Some(destroy), + }); +} + +pub fn use_hook_with_cleanup( + hook: impl FnOnce() -> T, + cleanup: impl FnOnce(T) + 'static, +) -> T { + let value = use_hook(hook); + let _value = value.clone(); + use_drop(move || cleanup(_value)); + value +} diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 2cb783ad3..907844184 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -89,9 +89,10 @@ pub mod prelude { consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, generation, has_context, needs_update, parent_scope, provide_context, provide_root_context, remove_future, schedule_update, schedule_update_any, spawn, spawn_forever, suspend, - try_consume_context, use_error_boundary, use_hook, AnyValue, Attribute, Component, - ComponentFunction, Element, ErrorBoundary, Event, EventHandler, Fragment, HasAttributes, - IntoAttributeValue, IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, ScopeState, - Task, Template, TemplateAttribute, TemplateNode, Throw, VNode, VNodeInner, VirtualDom, + try_consume_context, use_drop, use_error_boundary, use_hook, use_hook_with_cleanup, + AnyValue, Attribute, Component, ComponentFunction, Element, ErrorBoundary, Event, + EventHandler, Fragment, HasAttributes, IntoAttributeValue, IntoDynNode, Properties, + Runtime, RuntimeGuard, ScopeId, ScopeState, Task, Template, TemplateAttribute, + TemplateNode, Throw, VNode, VNodeInner, VirtualDom, }; } diff --git a/packages/desktop/src/hooks.rs b/packages/desktop/src/hooks.rs index a6de4a203..4e6411607 100644 --- a/packages/desktop/src/hooks.rs +++ b/packages/desktop/src/hooks.rs @@ -5,10 +5,10 @@ use crate::{ ShortcutHandle, ShortcutRegistryError, WryEventHandler, }; use dioxus_core::{ - prelude::{consume_context, current_scope_id}, + prelude::{consume_context, current_scope_id, use_hook_with_cleanup}, use_hook, }; -use dioxus_hooks::use_hook_with_cleanup; + use tao::{event::Event, event_loop::EventLoopWindowTarget}; use wry::RequestAsyncResponder; diff --git a/packages/hooks/src/use_future.rs b/packages/hooks/src/use_future.rs index f352aebf1..1a95c03f3 100644 --- a/packages/hooks/src/use_future.rs +++ b/packages/hooks/src/use_future.rs @@ -7,21 +7,14 @@ use dioxus_signals::*; 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. +/// A hook that allows you to spawn a future /// -/// 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. -/// -/// -pub fn use_future(mut future: impl FnMut() -> F) -> UseFuture +/// Does not regenerate the future when dependencies change. If you're looking for a future that does, check out +/// `use_resource` instead. +pub fn use_future(mut future: impl FnMut() -> F) -> UseFuture where - T: 'static, - F: Future + 'static, + F: Future + 'static, { - let mut value = use_signal(|| None); let mut state = use_signal(|| UseFutureState::Pending); let task = use_signal(|| { @@ -47,22 +40,21 @@ where .await; // Set the value - value.set(Some(res)); + // value.set(Some(res)); }); Some(task) }); - UseFuture { task, value, state } + UseFuture { task, state } } -pub struct UseFuture { - value: Signal>, +pub struct UseFuture { task: Signal>, - state: Signal>, + state: Signal, } -impl UseFuture { +impl UseFuture { /// Restart the future with new dependencies. /// /// Will not cancel the previous future, but will ignore any values that it @@ -79,18 +71,6 @@ impl UseFuture { // } } - // 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> { - self.value - } - /// Get the ID of the future in Dioxus' internal scheduler pub fn task(&self) -> Option { todo!() @@ -98,7 +78,7 @@ impl UseFuture { } /// Get the current state of the future. - pub fn state(&self) -> UseFutureState { + pub fn state(&self) -> UseFutureState { todo!() // match (&self.task.get(), &self.value()) { // // If we have a task and an existing value, we're reloading @@ -116,8 +96,8 @@ impl UseFuture { } } -pub enum UseFutureState { +pub enum UseFutureState { Pending, - Complete(Signal), - Regenerating(Signal), // the old value + Complete, + Regenerating, // the old value } diff --git a/packages/hooks/src/use_on_destroy.rs b/packages/hooks/src/use_on_destroy.rs index c38b1fd7d..7e215f9ed 100644 --- a/packages/hooks/src/use_on_destroy.rs +++ b/packages/hooks/src/use_on_destroy.rs @@ -1,4 +1,4 @@ -use dioxus_core::use_hook; +use dioxus_core::{prelude::use_drop, use_hook}; #[deprecated( note = "Use `use_on_destroy` instead, which has the same functionality. \ @@ -6,107 +6,5 @@ This is deprecated because of the introduction of `use_on_create` which is bette The reason why `use_on_create` is not `use_on_mount` is because of potential confusion with `dioxus::events::onmounted`." )] pub fn use_on_unmount(destroy: D) { - use_on_destroy(destroy); -} - -/// Creates a callback that will be run before the component is removed. -/// This can be used to clean up side effects from the component -/// (created with [`use_effect`](crate::use_effect)). -/// -/// Example: -/// ```rust -/// use dioxus::prelude::*; -/// -/// fn app() -> Element { -/// let state = use_signal(|| true); -/// rsx! { -/// for _ in 0..100 { -/// h1 { -/// "spacer" -/// } -/// } -/// if **state { -/// rsx! { -/// child_component {} -/// } -/// } -/// button { -/// onclick: move |_| { -/// state.set(!*state.get()); -/// }, -/// "Unmount element" -/// } -/// } -/// } -/// -/// fn child_component() -> Element { -/// let original_scroll_position = use_signal(|| 0.0); -/// use_effect((), move |_| { -/// to_owned![original_scroll_position]; -/// async move { -/// let window = web_sys::window().unwrap(); -/// let document = window.document().unwrap(); -/// let element = document.get_element_by_id("my_element").unwrap(); -/// element.scroll_into_view(); -/// original_scroll_position.set(window.scroll_y().unwrap()); -/// } -/// }); -/// -/// use_on_destroy({ -/// to_owned![original_scroll_position]; -/// /// restore scroll to the top of the page -/// move || { -/// let window = web_sys::window().unwrap(); -/// window.scroll_with_x_and_y(*original_scroll_position.current(), 0.0); -/// } -/// }); -/// -/// rsx!{ -/// div { -/// id: "my_element", -/// "hello" -/// } -/// } -/// } -/// ``` -pub fn use_on_destroy(destroy: D) { - struct LifeCycle { - /// Wrap the closure in an option so that we can take it out on drop. - ondestroy: Option, - } - - /// On drop, we want to run the closure. - impl Drop for LifeCycle { - fn drop(&mut self) { - if let Some(f) = self.ondestroy.take() { - f(); - } - } - } - - // We need to impl clone for the lifecycle, but we don't want the drop handler for the closure to be called twice. - impl Clone for LifeCycle { - fn clone(&self) -> Self { - Self { ondestroy: None } - } - } - - use_hook(|| LifeCycle { - ondestroy: Some(destroy), - }); -} - -/// Creates a callback that will be run before the component is dropped -pub fn use_on_drop(ondrop: D) { - use_on_destroy(ondrop); -} - -pub fn use_hook_with_cleanup( - hook: impl FnOnce() -> T, - cleanup: impl FnOnce(T) + 'static, -) -> T { - let value = use_hook(hook); - let _value = value.clone(); - use_on_destroy(move || cleanup(_value)); - value + use_drop(destroy); } diff --git a/packages/router/src/utils/use_router_internal.rs b/packages/router/src/utils/use_router_internal.rs index 7d00f821f..441063cbe 100644 --- a/packages/router/src/utils/use_router_internal.rs +++ b/packages/router/src/utils/use_router_internal.rs @@ -13,7 +13,7 @@ use crate::prelude::*; pub(crate) fn use_router_internal() -> Option { let router = use_hook(consume_context::); let id = current_scope_id().expect("use_router_internal called outside of a component"); - use_on_destroy({ + use_drop({ to_owned![router]; move || { router.unsubscribe(id);