From dd4547d75335f8b3eed8ed7f8606fba5703a6432 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 23 Oct 2023 16:26:10 -0400 Subject: [PATCH 1/2] Feat: add global context functions Add functions like window() and router() to allow dynamically grabbing global contexts without have to use the hook variants. Deprecates the existing hook variants to discourage folks from adding more noise to their codebases. --- examples/button.rs | 18 ------------ examples/compose.rs | 11 ++----- examples/crm.rs | 4 +-- examples/eval.rs | 23 ++++++--------- examples/login_form.rs | 34 ++++++++++------------ examples/multiwindow.rs | 4 +-- examples/overlay.rs | 6 ++-- examples/window_zoom.rs | 4 +-- packages/desktop/src/desktop_context.rs | 9 ++++++ packages/desktop/src/lib.rs | 2 +- packages/html/src/eval.rs | 9 ++++++ packages/router/examples/simple_routes.rs | 7 ++--- packages/router/src/contexts/navigator.rs | 14 +++++++++ packages/router/src/hooks/use_navigator.rs | 1 + packages/router/src/hooks/use_router.rs | 7 ++++- packages/router/src/lib.rs | 2 ++ 16 files changed, 77 insertions(+), 78 deletions(-) delete mode 100644 examples/button.rs diff --git a/examples/button.rs b/examples/button.rs deleted file mode 100644 index f001ed5311..0000000000 --- a/examples/button.rs +++ /dev/null @@ -1,18 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - dioxus_desktop::launch(app); -} - -fn app(cx: Scope) -> Element { - cx.render(rsx! { - button { - onclick: |_| async move { - println!("hello, desktop!"); - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - println!("goodbye, desktop!"); - }, - "hello, desktop!" - } - }) -} diff --git a/examples/compose.rs b/examples/compose.rs index 3ee6d36ae3..f544b5a9cb 100644 --- a/examples/compose.rs +++ b/examples/compose.rs @@ -1,7 +1,6 @@ //! This example shows how to create a popup window and send data back to the parent window. use dioxus::prelude::*; -use dioxus_desktop::use_window; use futures_util::StreamExt; fn main() { @@ -9,7 +8,6 @@ fn main() { } fn app(cx: Scope) -> Element { - let window = use_window(cx); let emails_sent = use_ref(cx, Vec::new); let tx = use_coroutine(cx, |mut rx: UnboundedReceiver| { @@ -34,7 +32,7 @@ fn app(cx: Scope) -> Element { // this returns a weak reference to the other window // Be careful not to keep a strong reference to the other window or it will never be dropped // and the window will never close. - window.new_window(dom, Default::default()); + dioxus_desktop::window().new_window(dom, Default::default()); }, "Click to compose a new email" } @@ -57,7 +55,6 @@ struct ComposeProps { fn compose(cx: Scope) -> Element { let user_input = use_state(cx, String::new); - let window = use_window(cx); cx.render(rsx! { div { @@ -66,15 +63,13 @@ fn compose(cx: Scope) -> Element { button { onclick: move |_| { cx.props.app_tx.send(user_input.get().clone()); - window.close(); + dioxus_desktop::window().close(); }, "Click to send" } input { - oninput: move |e| { - user_input.set(e.value.clone()); - }, + oninput: move |e| user_input.set(e.value.clone()), value: "{user_input}" } } diff --git a/examples/crm.rs b/examples/crm.rs index d2cd981181..189f98e5a9 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -87,8 +87,6 @@ fn ClientAdd(cx: Scope) -> Element { let last_name = use_state(cx, String::new); let description = use_state(cx, String::new); - let navigator = use_navigator(cx); - cx.render(rsx! { h2 { "Add new Client" } @@ -103,7 +101,7 @@ fn ClientAdd(cx: Scope) -> Element { description: description.to_string(), }); - navigator.push(Route::ClientList {}); + dioxus_router::router().push(Route::ClientList {}); }, fieldset { diff --git a/examples/eval.rs b/examples/eval.rs index 54959df69f..65c8acbeb5 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -5,26 +5,21 @@ fn main() { } fn app(cx: Scope) -> Element { - let eval_provider = use_eval(cx); - - let future = use_future(cx, (), |_| { - to_owned![eval_provider]; - async move { - let eval = eval_provider( - r#" + let future = use_future(cx, (), |_| async move { + let eval = eval( + r#" dioxus.send("Hi from JS!"); let msg = await dioxus.recv(); console.log(msg); return "hello world"; "#, - ) - .unwrap(); + ) + .unwrap(); - eval.send("Hi from Rust!".into()).unwrap(); - let res = eval.recv().await.unwrap(); - println!("{:?}", eval.await); - res - } + eval.send("Hi from Rust!".into()).unwrap(); + let res = eval.recv().await.unwrap(); + println!("{:?}", eval.await); + res }); match future.value() { diff --git a/examples/login_form.rs b/examples/login_form.rs index 5ee47e4b5c..d14338c3d8 100644 --- a/examples/login_form.rs +++ b/examples/login_form.rs @@ -8,27 +8,25 @@ fn main() { } fn app(cx: Scope) -> Element { - let onsubmit = move |evt: FormEvent| { - cx.spawn(async move { - let resp = reqwest::Client::new() - .post("http://localhost:8080/login") - .form(&[ - ("username", &evt.values["username"]), - ("password", &evt.values["password"]), - ]) - .send() - .await; + let onsubmit = move |evt: FormEvent| async move { + let resp = reqwest::Client::new() + .post("http://localhost:8080/login") + .form(&[ + ("username", &evt.values["username"]), + ("password", &evt.values["password"]), + ]) + .send() + .await; - match resp { - // Parse data from here, such as storing a response token - Ok(_data) => println!("Login successful!"), + match resp { + // Parse data from here, such as storing a response token + Ok(_data) => println!("Login successful!"), - //Handle any errors from the fetch here - Err(_err) => { - println!("Login failed - you need a login server running on localhost:8080.") - } + //Handle any errors from the fetch here + Err(_err) => { + println!("Login failed - you need a login server running on localhost:8080.") } - }); + } }; cx.render(rsx! { diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index e2caeba801..c7ad2c6033 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -5,14 +5,12 @@ fn main() { } fn app(cx: Scope) -> Element { - let window = dioxus_desktop::use_window(cx); - cx.render(rsx! { div { button { onclick: move |_| { let dom = VirtualDom::new(popup); - window.new_window(dom, Default::default()); + dioxus_desktop::window().new_window(dom, Default::default()); }, "New Window" } diff --git a/examples/overlay.rs b/examples/overlay.rs index 0afd830beb..a4b9e7bfe8 100644 --- a/examples/overlay.rs +++ b/examples/overlay.rs @@ -1,13 +1,11 @@ use dioxus::prelude::*; -use dioxus_desktop::{tao::dpi::PhysicalPosition, use_window, LogicalSize, WindowBuilder}; +use dioxus_desktop::{tao::dpi::PhysicalPosition, LogicalSize, WindowBuilder}; fn main() { dioxus_desktop::launch_cfg(app, make_config()); } fn app(cx: Scope) -> Element { - let window = use_window(cx); - cx.render(rsx! { div { width: "100%", @@ -19,7 +17,7 @@ fn app(cx: Scope) -> Element { width: "100%", height: "10px", background_color: "black", - onmousedown: move |_| window.drag(), + onmousedown: move |_| dioxus_desktop::window().drag(), } "This is an overlay!" diff --git a/examples/window_zoom.rs b/examples/window_zoom.rs index e45d71be24..bf0949d1fb 100644 --- a/examples/window_zoom.rs +++ b/examples/window_zoom.rs @@ -1,12 +1,10 @@ use dioxus::prelude::*; -use dioxus_desktop::use_window; fn main() { dioxus_desktop::launch(app); } fn app(cx: Scope) -> Element { - let window = use_window(cx); let level = use_state(cx, || 1.0); cx.render(rsx! { @@ -16,7 +14,7 @@ fn app(cx: Scope) -> Element { oninput: |e| { if let Ok(new_zoom) = e.value.parse::() { level.set(new_zoom); - window.webview.zoom(new_zoom); + dioxus_desktop::window().webview.zoom(new_zoom); } } } diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 897f8eba8b..22f6dbf8f3 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -28,6 +28,15 @@ use wry::webview::WebView; pub type ProxyType = EventLoopProxy; +/// Get an imperative handle to the current window without using a hook +/// +/// ## Panics +/// +/// This function will panic if it is called outside of the context of a Dioxus App. +pub fn window() -> DesktopContext { + dioxus_core::prelude::consume_context().unwrap() +} + /// Get an imperative handle to the current window pub fn use_window(cx: &ScopeState) -> &DesktopContext { cx.use_hook(|| cx.consume_context::()) diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index badbe32760..1aac7dfcb4 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -23,7 +23,7 @@ use crate::query::QueryResult; pub use cfg::{Config, WindowCloseBehaviour}; pub use desktop_context::DesktopContext; pub use desktop_context::{ - use_window, use_wry_event_handler, DesktopService, WryEventHandler, WryEventHandlerId, + use_window, use_wry_event_handler, window, DesktopService, WryEventHandler, WryEventHandlerId, }; use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers}; use dioxus_core::*; diff --git a/packages/html/src/eval.rs b/packages/html/src/eval.rs index 292de70fcc..394713af6e 100644 --- a/packages/html/src/eval.rs +++ b/packages/html/src/eval.rs @@ -48,6 +48,15 @@ pub fn use_eval(cx: &ScopeState) -> &EvalCreator { }) } +pub fn eval(script: &str) -> Result { + let eval_provider = dioxus_core::prelude::consume_context::>() + .expect("evaluator not provided"); + + eval_provider + .new_evaluator(script.to_string()) + .map(UseEval::new) +} + /// A wrapper around the target platform's evaluator. #[derive(Clone)] pub struct UseEval { diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs index 3634f7df38..d7738beab9 100644 --- a/packages/router/examples/simple_routes.rs +++ b/packages/router/examples/simple_routes.rs @@ -66,7 +66,6 @@ fn Route2(cx: Scope, user_id: usize) -> Element { #[component] fn Route3(cx: Scope, dynamic: String) -> Element { - let navigator = use_navigator(cx); let current_route = use_route(cx)?; let current_route_str = use_ref(cx, String::new); let parsed = Route::from_str(¤t_route_str.read()); @@ -78,9 +77,7 @@ fn Route3(cx: Scope, dynamic: String) -> Element { render! { input { - oninput: move |evt| { - *current_route_str.write() = evt.value.clone(); - }, + oninput: move |evt| *current_route_str.write() = evt.value.clone(), value: "{current_route_str.read()}" } "dynamic: {dynamic}" @@ -89,7 +86,7 @@ fn Route3(cx: Scope, dynamic: String) -> Element { "hello world link" } button { - onclick: move |_| { navigator.push("https://www.google.com"); }, + onclick: move |_| { dioxus_router::router().push("https://www.google.com"); }, "google link" } p { "Site Map" } diff --git a/packages/router/src/contexts/navigator.rs b/packages/router/src/contexts/navigator.rs index 961122c052..8c8bdd1093 100644 --- a/packages/router/src/contexts/navigator.rs +++ b/packages/router/src/contexts/navigator.rs @@ -1,5 +1,19 @@ use crate::prelude::{ExternalNavigationFailure, IntoRoutable, RouterContext}; +/// Acquire the navigator without subscribing to updates. +/// +/// Can be called anywhere in the application provided a Router has been initialized. +/// +/// ## Panics +/// +/// Panics if there is no router present. +pub fn navigator() -> Navigator { + Navigator( + dioxus::core::prelude::consume_context::() + .expect("A router must be present to use navigator"), + ) +} + /// A view into the navigation state of a router. #[derive(Clone)] pub struct Navigator(pub(crate) RouterContext); diff --git a/packages/router/src/hooks/use_navigator.rs b/packages/router/src/hooks/use_navigator.rs index a05cd036f0..9d367d83d6 100644 --- a/packages/router/src/hooks/use_navigator.rs +++ b/packages/router/src/hooks/use_navigator.rs @@ -49,6 +49,7 @@ use crate::prelude::{Navigator, RouterContext}; /// # let _ = vdom.rebuild(); /// ``` #[must_use] +#[deprecated = "Prefer acquiring the router directly with `dioxus_router::router()`"] pub fn use_navigator(cx: &ScopeState) -> &Navigator { &*cx.use_hook(|| { let router = cx diff --git a/packages/router/src/hooks/use_router.rs b/packages/router/src/hooks/use_router.rs index 5f1f13e452..2cb628160c 100644 --- a/packages/router/src/hooks/use_router.rs +++ b/packages/router/src/hooks/use_router.rs @@ -2,7 +2,7 @@ use dioxus::prelude::ScopeState; use crate::{prelude::RouterContext, utils::use_router_internal::use_router_internal}; -#[deprecated = "prefer the use_navigator or use_route functions"] +#[deprecated = "prefer the `router()` function or `use_route` functions"] #[must_use] /// A hook that provides access to information about the router. pub fn use_router(cx: &ScopeState) -> &RouterContext { @@ -10,3 +10,8 @@ pub fn use_router(cx: &ScopeState) -> &RouterContext { .as_ref() .expect("use_route must have access to a router") } + +/// Aquire the router without subscribing to updates. +pub fn router() -> RouterContext { + dioxus::core::prelude::consume_context().unwrap() +} diff --git a/packages/router/src/lib.rs b/packages/router/src/lib.rs index e871dc6c95..a1432d5b98 100644 --- a/packages/router/src/lib.rs +++ b/packages/router/src/lib.rs @@ -53,6 +53,8 @@ pub mod hooks { pub use use_navigator::*; } +pub use hooks::router; + /// A collection of useful items most applications might need. pub mod prelude { pub use crate::components::*; From 1fe8e940637ac151e5f93af2581cc0cc947253b0 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 23 Oct 2023 16:30:39 -0400 Subject: [PATCH 2/2] Deprecate use_window --- packages/desktop/src/desktop_context.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 22f6dbf8f3..74a2da8b00 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -38,6 +38,7 @@ pub fn window() -> DesktopContext { } /// Get an imperative handle to the current window +#[deprecated = "Prefer the using the `window` function directly for cleaner code"] pub fn use_window(cx: &ScopeState) -> &DesktopContext { cx.use_hook(|| cx.consume_context::()) .as_ref()