From 3865f440818b9d28d66c40835567566e3e4ef45a Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 5 Jan 2024 14:39:45 -0600 Subject: [PATCH] make desktop compile --- packages/core/src/error_boundary.rs | 16 +- packages/core/src/events.rs | 2 +- packages/core/src/global_context.rs | 115 ++++++++++ packages/core/src/lib.rs | 17 +- packages/core/src/nodes.rs | 3 +- packages/core/src/runtime.rs | 10 +- packages/core/src/scheduler/mod.rs | 2 +- packages/core/src/scheduler/task.rs | 37 +++- packages/core/src/scheduler/wait.rs | 4 +- packages/core/src/scope_arena.rs | 2 +- packages/core/src/scope_context.rs | 205 ++++++------------ packages/core/src/scopes.rs | 4 +- packages/core/src/virtual_dom.rs | 17 +- packages/desktop/src/desktop_context.rs | 34 ++- packages/desktop/src/eval.rs | 9 +- packages/desktop/src/lib.rs | 200 ++++------------- packages/desktop/src/protocol.rs | 12 +- packages/desktop/src/shortcut.rs | 17 +- packages/fullstack/src/hooks/server_future.rs | 4 +- packages/hooks/src/use_coroutine.rs | 6 +- packages/hooks/src/use_effect.rs | 10 +- packages/hooks/src/use_future.rs | 6 +- packages/hot-reload/src/lib.rs | 4 +- packages/html/src/eval.rs | 20 +- packages/html/src/events.rs | 12 +- packages/html/src/events/mouse.rs | 9 +- packages/router/examples/simple_routes.rs | 2 +- 27 files changed, 357 insertions(+), 422 deletions(-) create mode 100644 packages/core/src/global_context.rs diff --git a/packages/core/src/error_boundary.rs b/packages/core/src/error_boundary.rs index e2b81338b..973f164d1 100644 --- a/packages/core/src/error_boundary.rs +++ b/packages/core/src/error_boundary.rs @@ -1,5 +1,5 @@ use crate::{ - scope_context::{consume_context, current_scope_id, schedule_update_any}, + global_context::{consume_context, current_scope_id}, Element, IntoDynNode, Properties, ScopeId, Template, TemplateAttribute, TemplateNode, VNode, }; use std::{ @@ -9,7 +9,6 @@ use std::{ error::Error, fmt::{Debug, Display}, rc::Rc, - sync::Arc, }; /// Provide an error boundary to catch errors from child components @@ -28,7 +27,6 @@ pub struct ErrorBoundary { pub struct ErrorBoundaryInner { error: RefCell>, _id: ScopeId, - rerun_boundary: Arc, } impl Debug for ErrorBoundaryInner { @@ -81,7 +79,6 @@ impl Default for ErrorBoundaryInner { error: RefCell::new(None), _id: current_scope_id() .expect("Cannot create an error boundary outside of a component's scope."), - rerun_boundary: schedule_update_any().unwrap(), } } } @@ -93,15 +90,11 @@ impl ErrorBoundary { } /// Create a new error boundary in the current scope - pub(crate) fn new_in_scope( - scope: ScopeId, - rerun_boundary: Arc, - ) -> Self { + pub(crate) fn new_in_scope(scope: ScopeId) -> Self { Self { inner: Rc::new(ErrorBoundaryInner { error: RefCell::new(None), _id: scope, - rerun_boundary, }), } } @@ -113,13 +106,14 @@ impl ErrorBoundary { error: Box, backtrace: Backtrace, ) { - println!("{:?} {:?}", error, self.inner._id); self.inner.error.replace(Some(CapturedError { error, scope, backtrace, })); - (self.inner.rerun_boundary)(self.inner._id); + if self.inner._id != ScopeId::ROOT { + self.inner._id.needs_update(); + } } /// Take any error that has been captured by this error boundary diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 5cd4665e9..d8745a778 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -1,4 +1,4 @@ -use crate::{runtime::with_runtime, scope_context::current_scope_id, ScopeId}; +use crate::{global_context::current_scope_id, runtime::with_runtime, ScopeId}; use std::{ cell::{Cell, RefCell}, rc::Rc, diff --git a/packages/core/src/global_context.rs b/packages/core/src/global_context.rs new file mode 100644 index 000000000..768348cea --- /dev/null +++ b/packages/core/src/global_context.rs @@ -0,0 +1,115 @@ +use futures_util::Future; + +use crate::{ + runtime::{with_current_scope, with_runtime}, + Element, ScopeId, Task, +}; + +/// Get the current scope id +pub fn current_scope_id() -> Option { + with_runtime(|rt| rt.current_scope_id()).flatten() +} + +#[doc(hidden)] +/// Check if the virtual dom is currently inside of the body of a component +pub fn vdom_is_rendering() -> bool { + with_runtime(|rt| rt.rendering.get()).unwrap_or_default() +} + +/// Consume context from the current scope +pub fn consume_context() -> Option { + with_current_scope(|cx| cx.consume_context::()).flatten() +} + +/// Consume context from the current scope +pub fn consume_context_from_scope(scope_id: ScopeId) -> Option { + with_runtime(|rt| { + rt.get_context(scope_id) + .and_then(|cx| cx.consume_context::()) + }) + .flatten() +} + +/// Check if the current scope has a context +pub fn has_context() -> Option { + with_current_scope(|cx| cx.has_context::()).flatten() +} + +/// Provide context to the current scope +pub fn provide_context(value: T) -> Option { + with_current_scope(|cx| cx.provide_context(value)) +} + +/// Provide a context to the root scope +pub fn provide_root_context(value: T) -> Option { + with_current_scope(|cx| cx.provide_root_context(value)) +} + +/// Suspends the current component +pub fn suspend() -> Option { + with_current_scope(|cx| { + cx.suspend(); + }); + None +} + +/// Pushes the future onto the poll queue to be polled after the component renders. +pub fn push_future(fut: impl Future + 'static) -> Option { + with_current_scope(|cx| cx.push_future(fut)) +} + +/// Spawns the future but does not return the [`TaskId`] +pub fn spawn(fut: impl Future + 'static) { + with_current_scope(|cx| cx.spawn(fut)); +} + +/// Spawn a future that Dioxus won't clean up when this component is unmounted +/// +/// This is good for tasks that need to be run after the component has been dropped. +pub fn spawn_forever(fut: impl Future + 'static) -> Option { + with_current_scope(|cx| cx.spawn_forever(fut)) +} + +/// Informs the scheduler that this task is no longer needed and should be removed. +/// +/// This drops the task immediately. +pub fn remove_future(id: Task) { + with_current_scope(|cx| cx.remove_future(id)); +} + +/// Store a value between renders. The foundational hook for all other hooks. +/// +/// Accepts an `initializer` closure, which is run on the first use of the hook (typically the initial render). The return value of this closure is stored for the lifetime of the component, and a mutable reference to it is provided on every render as the return value of `use_hook`. +/// +/// When the component is unmounted (removed from the UI), the value is dropped. This means you can return a custom type and provide cleanup code by implementing the [`Drop`] trait +/// +/// # Example +/// +/// ``` +/// use dioxus_core::ScopeState; +/// +/// // prints a greeting on the initial render +/// pub fn use_hello_world() { +/// once(|| println!("Hello, world!")); +/// } +/// ``` +pub fn once(initializer: impl FnOnce() -> State) -> State { + with_current_scope(|cx| cx.use_hook(initializer)).expect("to be in a dioxus runtime") +} + +/// Get the current render since the inception of this component +/// +/// This can be used as a helpful diagnostic when debugging hooks/renders, etc +pub fn generation() -> Option { + with_current_scope(|cx| Some(cx.generation())).expect("to be in a dioxus runtime") +} + +/// Get the parent of the current scope if it exists +pub fn parent_scope() -> Option { + with_current_scope(|cx| cx.parent_id()).flatten() +} + +/// Mark the current scope as dirty, causing it to re-render +pub fn needs_update() { + with_current_scope(|cx| cx.needs_update()); +} diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 88dc25ca3..09c17e510 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -11,6 +11,7 @@ mod dirty_scope; mod error_boundary; mod events; mod fragment; +mod global_context; mod mutations; mod nodes; mod properties; @@ -27,13 +28,13 @@ pub(crate) mod innerlude { pub use crate::error_boundary::*; pub use crate::events::*; pub use crate::fragment::*; + pub use crate::global_context::*; pub use crate::mutations::*; pub use crate::nodes::RenderReturn; pub use crate::nodes::*; pub use crate::properties::*; pub use crate::runtime::{Runtime, RuntimeGuard}; pub use crate::scheduler::*; - pub use crate::scope_context::*; pub use crate::scopes::*; pub use crate::virtual_dom::*; @@ -75,8 +76,8 @@ pub(crate) mod innerlude { pub use crate::innerlude::{ fc_to_builder, generation, once, vdom_is_rendering, AnyValue, Attribute, AttributeValue, CapturedError, Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, - Mutation, MutationsVec, Properties, RenderReturn, ScopeId, ScopeState, TaskId, Template, - TemplateAttribute, TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom, + Mutation, MutationsVec, Properties, RenderReturn, ScopeId, Task, Template, TemplateAttribute, + TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom, WriteMutations, }; /// The purpose of this module is to alleviate imports of many common types @@ -85,10 +86,10 @@ pub use crate::innerlude::{ pub mod prelude { pub use crate::innerlude::{ consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, generation, - has_context, once, provide_context, provide_context_to_scope, provide_root_context, - push_future, remove_future, schedule_update_any, spawn, spawn_forever, suspend, - use_error_boundary, AnyValue, Component, Element, ErrorBoundary, Event, EventHandler, - Fragment, IntoAttributeValue, IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, - TaskId, Template, TemplateAttribute, TemplateNode, Throw, VNode, VirtualDom, + has_context, needs_update, once, parent_scope, provide_context, provide_root_context, + push_future, remove_future, spawn, spawn_forever, suspend, use_error_boundary, AnyValue, + Component, Element, ErrorBoundary, Event, EventHandler, Fragment, IntoAttributeValue, + IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, Task, Template, TemplateAttribute, + TemplateNode, Throw, VNode, VirtualDom, }; } diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index f48609edf..807509f0c 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -518,8 +518,7 @@ impl Attribute { /// /// "Volatile" referes to whether or not Dioxus should always override the value. This helps prevent the UI in /// some renderers stay in sync with the VirtualDom's understanding of the world - pub fn attr( - &self, + pub fn new( name: &'static str, value: impl IntoAttributeValue, namespace: Option<&'static str>, diff --git a/packages/core/src/runtime.rs b/packages/core/src/runtime.rs index 566e9ba7c..c9e6b88ac 100644 --- a/packages/core/src/runtime.rs +++ b/packages/core/src/runtime.rs @@ -41,6 +41,14 @@ where .flatten() } +/// Runs a function with the current scope +pub(crate) fn with_scope(scope: ScopeId, f: F) -> Option +where + F: FnOnce(&ScopeContext) -> R, +{ + with_runtime(|runtime| runtime.get_context(scope).map(|sc| f(&sc))).flatten() +} + /// A global runtime that is shared across all scopes that provides the async runtime and context API pub struct Runtime { pub(crate) scope_contexts: RefCell>>, @@ -98,7 +106,7 @@ impl Runtime { } } -/// A gaurd for a new runtime. This must be used to override the current runtime when importing components from a dynamic library that has it's own runtime. +/// A guard for a new runtime. This must be used to override the current runtime when importing components from a dynamic library that has it's own runtime. /// /// ```rust /// use dioxus::prelude::*; diff --git a/packages/core/src/scheduler/mod.rs b/packages/core/src/scheduler/mod.rs index a13958363..0446ae91b 100644 --- a/packages/core/src/scheduler/mod.rs +++ b/packages/core/src/scheduler/mod.rs @@ -15,7 +15,7 @@ pub(crate) enum SchedulerMsg { Immediate(ScopeId), /// A task has woken and needs to be progressed - TaskNotified(TaskId), + TaskNotified(Task), } use std::{cell::RefCell, rc::Rc}; diff --git a/packages/core/src/scheduler/task.rs b/packages/core/src/scheduler/task.rs index d2e9135e6..c6223e5ea 100644 --- a/packages/core/src/scheduler/task.rs +++ b/packages/core/src/scheduler/task.rs @@ -1,6 +1,7 @@ use futures_util::task::ArcWake; use super::{Scheduler, SchedulerMsg}; +use crate::innerlude::{push_future, remove_future}; use crate::ScopeId; use std::cell::RefCell; use std::future::Future; @@ -14,7 +15,29 @@ use std::task::Waker; /// once a Task has been completed. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct TaskId(pub usize); +pub struct Task(pub(crate) usize); + +impl Task { + /// Start a new future on the same thread as the rest of the VirtualDom. + /// + /// This future will not contribute to suspense resolving, so you should primarily use this for reacting to changes + /// and long running tasks. + /// + /// Whenever the component that owns this future is dropped, the future will be dropped as well. + /// + /// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which + /// will only occur when the VirtualDom itself has been dropped. + pub fn new(task: impl Future + 'static) -> Self { + push_future(task).expect("to be in a dioxus runtime") + } + + /// Drop the task immediately. + /// + /// This does not abort the task, so you'll want to wrap it in an abort handle if that's important to you + pub fn stop(self) { + remove_future(self); + } +} /// the task itself is the waker pub(crate) struct LocalTask { @@ -32,12 +55,12 @@ impl Scheduler { /// Whenever the component that owns this future is dropped, the future will be dropped as well. /// /// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which - /// will only occur when the VirtuaalDom itself has been dropped. - pub fn spawn(&self, scope: ScopeId, task: impl Future + 'static) -> TaskId { + /// will only occur when the VirtualDom itself has been dropped. + pub fn spawn(&self, scope: ScopeId, task: impl Future + 'static) -> Task { let mut tasks = self.tasks.borrow_mut(); let entry = tasks.vacant_entry(); - let task_id = TaskId(entry.key()); + let task_id = Task(entry.key()); let task = LocalTask { task: RefCell::new(Box::pin(task)), @@ -63,14 +86,14 @@ impl Scheduler { /// Drop the future with the given TaskId /// - /// This does not abort the task, so you'll want to wrap it in an aborthandle if that's important to you - pub fn remove(&self, id: TaskId) -> Option { + /// This does not abort the task, so you'll want to wrap it in an abort handle if that's important to you + pub fn remove(&self, id: Task) -> Option { self.tasks.borrow_mut().try_remove(id.0) } } pub struct LocalTaskHandle { - id: TaskId, + id: Task, tx: futures_channel::mpsc::UnboundedSender, } diff --git a/packages/core/src/scheduler/wait.rs b/packages/core/src/scheduler/wait.rs index 625873293..9d6969847 100644 --- a/packages/core/src/scheduler/wait.rs +++ b/packages/core/src/scheduler/wait.rs @@ -1,4 +1,4 @@ -use crate::{runtime::RuntimeGuard, TaskId, VirtualDom}; +use crate::{runtime::RuntimeGuard, Task, VirtualDom}; use std::task::Context; impl VirtualDom { @@ -6,7 +6,7 @@ impl VirtualDom { /// /// This is precise, meaning we won't poll every task, just tasks that have woken up as notified to use by the /// queue - pub(crate) fn handle_task_wakeup(&mut self, id: TaskId) { + pub(crate) fn handle_task_wakeup(&mut self, id: Task) { let _runtime = RuntimeGuard::new(self.runtime.clone()); let mut tasks = self.runtime.scheduler.tasks.borrow_mut(); diff --git a/packages/core/src/scope_arena.rs b/packages/core/src/scope_arena.rs index 20cba5480..ad2c48b73 100644 --- a/packages/core/src/scope_arena.rs +++ b/packages/core/src/scope_arena.rs @@ -11,7 +11,7 @@ impl VirtualDom { pub(super) fn new_scope(&mut self, props: BoxedAnyProps, name: &'static str) -> &ScopeState { let parent_id = self.runtime.current_scope_id(); let height = parent_id - .and_then(|parent_id| self.get_scope(parent_id).map(|f| f.context().height + 1)) + .and_then(|parent_id| self.runtime.get_context(parent_id).map(|f| f.height + 1)) .unwrap_or(0); let entry = self.scopes.vacant_entry(); let id = ScopeId(entry.key()); diff --git a/packages/core/src/scope_context.rs b/packages/core/src/scope_context.rs index 7a8039f37..54182ff92 100644 --- a/packages/core/src/scope_context.rs +++ b/packages/core/src/scope_context.rs @@ -1,7 +1,7 @@ use crate::{ innerlude::{Scheduler, SchedulerMsg}, - runtime::{with_current_scope, with_runtime}, - Element, ScopeId, TaskId, + runtime::{with_runtime, with_scope}, + Element, ScopeId, Task, }; use rustc_hash::FxHashSet; use std::{ @@ -9,7 +9,6 @@ use std::{ cell::{Cell, RefCell}, future::Future, rc::Rc, - sync::Arc, }; /// A component's state separate from its props. @@ -32,7 +31,7 @@ pub(crate) struct ScopeContext { pub(crate) hook_index: Cell, pub(crate) tasks: Rc, - pub(crate) spawned_tasks: RefCell>, + pub(crate) spawned_tasks: RefCell>, } impl ScopeContext { @@ -62,42 +61,11 @@ impl ScopeContext { self.parent_id } - pub fn scope_id(&self) -> ScopeId { - self.id - } - - /// Create a subscription that schedules a future render for the reference component - /// - /// ## Notice: you should prefer using [`Self::schedule_update_any`] and [`Self::scope_id`] - pub fn schedule_update(&self) -> Arc { - let (chan, id) = (self.tasks.sender.clone(), self.scope_id()); - Arc::new(move || drop(chan.unbounded_send(SchedulerMsg::Immediate(id)))) - } - - /// Schedule an update for any component given its [`ScopeId`]. - /// - /// A component's [`ScopeId`] can be obtained from `use_hook` or the [`ScopeState::scope_id`] method. - /// - /// This method should be used when you want to schedule an update for a component - pub fn schedule_update_any(&self) -> Arc { - let chan = self.tasks.sender.clone(); - Arc::new(move |id| { - chan.unbounded_send(SchedulerMsg::Immediate(id)).unwrap(); - }) - } - /// Mark this scope as dirty, and schedule a render for it. pub fn needs_update(&self) { - self.needs_update_any(self.scope_id()); - } - - /// Get the [`ScopeId`] of a mounted component. - /// - /// `ScopeId` is not unique for the lifetime of the [`crate::VirtualDom`] - a [`ScopeId`] will be reused if a component is unmounted. - pub fn needs_update_any(&self, id: ScopeId) { self.tasks .sender - .unbounded_send(SchedulerMsg::Immediate(id)) + .unbounded_send(SchedulerMsg::Immediate(self.id)) .expect("Scheduler to exist if scope exists"); } @@ -221,7 +189,7 @@ impl ScopeContext { } /// Pushes the future onto the poll queue to be polled after the component renders. - pub fn push_future(&self, fut: impl Future + 'static) -> TaskId { + pub fn push_future(&self, fut: impl Future + 'static) -> Task { let id = self.tasks.spawn(self.id, fut); self.spawned_tasks.borrow_mut().insert(id); id @@ -235,7 +203,7 @@ impl ScopeContext { /// Spawn a future that Dioxus won't clean up when this component is unmounted /// /// This is good for tasks that need to be run after the component has been dropped. - pub fn spawn_forever(&self, fut: impl Future + 'static) -> TaskId { + pub fn spawn_forever(&self, fut: impl Future + 'static) -> Task { // The root scope will never be unmounted so we can just add the task at the top of the app self.tasks.spawn(ScopeId::ROOT, fut) } @@ -243,7 +211,7 @@ impl ScopeContext { /// Informs the scheduler that this task is no longer needed and should be removed. /// /// This drops the task immediately. - pub fn remove_future(&self, id: TaskId) { + pub fn remove_future(&self, id: Task) { self.tasks.remove(id); } @@ -317,115 +285,74 @@ impl Drop for ScopeContext { } } -/// Schedule an update for any component given its [`ScopeId`]. -/// -/// A component's [`ScopeId`] can be obtained from `use_hook` or the [`crate::scopes::ScopeState::scope_id`] method. -/// -/// This method should be used when you want to schedule an update for a component -pub fn schedule_update_any() -> Option> { - with_current_scope(|cx| cx.schedule_update_any()) -} +impl ScopeId { + /// Get the current scope id + pub fn current_scope_id(self) -> Option { + with_runtime(|rt| rt.current_scope_id()).flatten() + } -/// Get the current scope id -pub fn current_scope_id() -> Option { - with_runtime(|rt| rt.current_scope_id()).flatten() -} + #[doc(hidden)] + /// Check if the virtual dom is currently inside of the body of a component + pub fn vdom_is_rendering(self) -> bool { + with_runtime(|rt| rt.rendering.get()).unwrap_or_default() + } -#[doc(hidden)] -/// Check if the virtual dom is currently inside of the body of a component -pub fn vdom_is_rendering() -> bool { - with_runtime(|rt| rt.rendering.get()).unwrap_or_default() -} + /// Consume context from the current scope + pub fn consume_context(self) -> Option { + with_scope(self, |cx| cx.consume_context::()).flatten() + } -/// Consume context from the current scope -pub fn consume_context() -> Option { - with_current_scope(|cx| cx.consume_context::()).flatten() -} + /// Consume context from the current scope + pub fn consume_context_from_scope(self, scope_id: ScopeId) -> Option { + with_runtime(|rt| { + rt.get_context(scope_id) + .and_then(|cx| cx.consume_context::()) + }) + .flatten() + } -/// Consume context from the current scope -pub fn consume_context_from_scope(scope_id: ScopeId) -> Option { - with_runtime(|rt| { - rt.get_context(scope_id) - .and_then(|cx| cx.consume_context::()) - }) - .flatten() -} + /// Check if the current scope has a context + pub fn has_context(self) -> Option { + with_scope(self, |cx| cx.has_context::()).flatten() + } -/// Check if the current scope has a context -pub fn has_context() -> Option { - with_current_scope(|cx| cx.has_context::()).flatten() -} + /// Provide context to the current scope + pub fn provide_context(self, value: T) -> Option { + with_scope(self, |cx| cx.provide_context(value)) + } -/// Provide context to the current scope -pub fn provide_context(value: T) -> Option { - with_current_scope(|cx| cx.provide_context(value)) -} + /// Suspends the current component + pub fn suspend(self) -> Option { + with_scope(self, |cx| { + cx.suspend(); + }); + None + } -/// Provide context to the the given scope -pub fn provide_context_to_scope(scope_id: ScopeId, value: T) -> Option { - with_runtime(|rt| rt.get_context(scope_id).map(|cx| cx.provide_context(value))).flatten() -} + /// Pushes the future onto the poll queue to be polled after the component renders. + pub fn push_future(self, fut: impl Future + 'static) -> Option { + with_scope(self, |cx| cx.push_future(fut)) + } -/// Provide a context to the root scope -pub fn provide_root_context(value: T) -> Option { - with_current_scope(|cx| cx.provide_root_context(value)) -} + /// Spawns the future but does not return the [`TaskId`] + pub fn spawn(self, fut: impl Future + 'static) { + with_scope(self, |cx| cx.spawn(fut)); + } -/// Suspends the current component -pub fn suspend() -> Option { - with_current_scope(|cx| { - cx.suspend(); - }); - None -} + /// Get the current render since the inception of this component + /// + /// This can be used as a helpful diagnostic when debugging hooks/renders, etc + pub fn generation(self) -> Option { + with_scope(self, |cx| Some(cx.generation())).expect("to be in a dioxus runtime") + } -/// Pushes the future onto the poll queue to be polled after the component renders. -pub fn push_future(fut: impl Future + 'static) -> Option { - with_current_scope(|cx| cx.push_future(fut)) -} + /// Get the parent of the current scope if it exists + pub fn parent_scope(self) -> Option { + with_scope(self, |cx| cx.parent_id()).flatten() + } -/// Spawns the future but does not return the [`TaskId`] -pub fn spawn(fut: impl Future + 'static) { - with_current_scope(|cx| cx.spawn(fut)); -} - -/// Spawn a future that Dioxus won't clean up when this component is unmounted -/// -/// This is good for tasks that need to be run after the component has been dropped. -pub fn spawn_forever(fut: impl Future + 'static) -> Option { - with_current_scope(|cx| cx.spawn_forever(fut)) -} - -/// Informs the scheduler that this task is no longer needed and should be removed. -/// -/// This drops the task immediately. -pub fn remove_future(id: TaskId) { - with_current_scope(|cx| cx.remove_future(id)); -} - -/// Store a value between renders. The foundational hook for all other hooks. -/// -/// Accepts an `initializer` closure, which is run on the first use of the hook (typically the initial render). The return value of this closure is stored for the lifetime of the component, and a mutable reference to it is provided on every render as the return value of `use_hook`. -/// -/// When the component is unmounted (removed from the UI), the value is dropped. This means you can return a custom type and provide cleanup code by implementing the [`Drop`] trait -/// -/// # Example -/// -/// ``` -/// use dioxus_core::ScopeState; -/// -/// // prints a greeting on the initial render -/// pub fn use_hello_world() { -/// once(|| println!("Hello, world!")); -/// } -/// ``` -pub fn once(initializer: impl FnOnce() -> State) -> State { - with_current_scope(|cx| cx.use_hook(initializer)).expect("to be in a dioxus runtime") -} - -/// Get the current render since the inception of this component -/// -/// This can be used as a helpful diagnostic when debugging hooks/renders, etc -pub fn generation() -> Option { - with_current_scope(|cx| Some(cx.generation())).expect("to be in a dioxus runtime") + /// Mark the current scope as dirty, causing it to re-render + pub fn needs_update(self) { + with_scope(self, |cx| cx.needs_update()); + } } diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index da8acb619..b15b6cc65 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -10,7 +10,7 @@ use std::{cell::Ref, fmt::Debug, rc::Rc}; /// time for any logic that relies on these IDs to properly update. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] -pub struct ScopeId(pub usize); +pub struct ScopeId(pub(crate) usize); impl ScopeId { /// The root ScopeId. @@ -30,7 +30,7 @@ impl ScopeId { /// A component's state separate from its props. /// /// This struct exists to provide a common interface for all scopes without relying on generics. -pub struct ScopeState { +pub(crate) struct ScopeState { pub(crate) runtime: Rc, pub(crate) context_id: ScopeId, diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 170261b0a..19142bad6 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -18,7 +18,7 @@ use crate::{ use futures_util::{pin_mut, StreamExt}; use rustc_hash::{FxHashMap, FxHashSet}; use slab::Slab; -use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc, sync::Arc}; +use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc}; /// A virtual node system that progresses user events and diffs UI trees. /// @@ -277,10 +277,7 @@ impl VirtualDom { // Unlike react, we provide a default error boundary that just renders the error as a string root.context() - .provide_context(Rc::new(ErrorBoundary::new_in_scope( - ScopeId::ROOT, - Arc::new(|_| {}), - ))); + .provide_context(Rc::new(ErrorBoundary::new_in_scope(ScopeId::ROOT))); // the root element is always given element ID 0 since it's the container for the entire tree dom.elements.insert(None); @@ -291,17 +288,23 @@ impl VirtualDom { /// Get the state for any scope given its ID /// /// This is useful for inserting or removing contexts from a scope, or rendering out its root node - pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeState> { + pub(crate) fn get_scope(&self, id: ScopeId) -> Option<&ScopeState> { self.scopes.get(id.0).map(|s| &**s) } /// Get the single scope at the top of the VirtualDom tree that will always be around /// /// This scope has a ScopeId of 0 and is the root of the tree - pub fn base_scope(&self) -> &ScopeState { + pub(crate) fn base_scope(&self) -> &ScopeState { self.get_scope(ScopeId::ROOT).unwrap() } + /// Run a closure inside the dioxus runtime + pub fn in_runtime(&self, f: impl FnOnce() -> O) -> O { + let _runtime = RuntimeGuard::new(self.runtime.clone()); + f() + } + /// Build the virtualdom with a global context inserted into the base scope /// /// This is useful for what is essentially dependency injection when building the app diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 62a574d9b..306e5fd76 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -7,11 +7,13 @@ use crate::shortcut::{HotKey, ShortcutId, ShortcutRegistry, ShortcutRegistryErro use crate::AssetHandler; use crate::Config; use crate::WebviewHandler; -use dioxus_core::ScopeState; +use dioxus_core::once; +use dioxus_core::ScopeId; use dioxus_core::VirtualDom; #[cfg(all(feature = "hot-reload", debug_assertions))] use dioxus_hot_reload::HotReloadMsg; use dioxus_interpreter_js::binary_protocol::Channel; +use dioxus_interpreter_js::MutationState; use rustc_hash::FxHashMap; use slab::Slab; use std::cell::RefCell; @@ -45,10 +47,8 @@ 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() - .unwrap() +pub fn use_window() -> DesktopContext { + once(window) } /// This handles communication between the requests that the webview makes and the interpreter. The interpreter constantly makes long running requests to the webview to get any edits that should be made to the DOM almost like server side events. @@ -123,10 +123,8 @@ pub struct DesktopService { pub(crate) shortcut_manager: ShortcutRegistry, pub(crate) edit_queue: EditQueue, - pub(crate) templates: RefCell>, - pub(crate) max_template_count: AtomicU16, + pub(crate) mutation_state: RefCell, - pub(crate) channel: RefCell, pub(crate) asset_handlers: AssetHandlerRegistry, #[cfg(target_os = "ios")] @@ -165,9 +163,7 @@ impl DesktopService { event_handlers, shortcut_manager, edit_queue, - templates: Default::default(), - max_template_count: Default::default(), - channel: Default::default(), + mutation_state: Default::default(), asset_handlers, #[cfg(target_os = "ios")] views: Default::default(), @@ -192,11 +188,11 @@ impl DesktopService { self.shortcut_manager.clone(), ); - let desktop_context = window - .dom - .base_scope() - .consume_context::>() - .unwrap(); + let desktop_context = window.dom.in_runtime(|| { + ScopeId::ROOT + .consume_context::>() + .unwrap() + }); let id = window.desktop_context.webview.window().id(); @@ -463,10 +459,9 @@ impl WryWindowEventHandlerInner { /// Get a closure that executes any JavaScript in the WebView context. pub fn use_wry_event_handler( - cx: &ScopeState, handler: impl FnMut(&Event, &EventLoopWindowTarget) + 'static, -) -> &WryEventHandler { - cx.use_hook(move || { +) -> WryEventHandler { + once(move || { let desktop = window(); let id = desktop.create_wry_event_handler(handler); @@ -481,6 +476,7 @@ pub fn use_wry_event_handler( /// A wry event handler that is scoped to the current component and window. The event handler will only receive events for the window it was created for and global events. /// /// This will automatically be removed when the component is unmounted. +#[derive(Clone)] pub struct WryEventHandler { handlers: WindowEventHandlers, /// The unique identifier of the event handler. diff --git a/packages/desktop/src/eval.rs b/packages/desktop/src/eval.rs index e187be450..d0419e8c1 100644 --- a/packages/desktop/src/eval.rs +++ b/packages/desktop/src/eval.rs @@ -1,16 +1,17 @@ #![allow(clippy::await_holding_refcell_ref)] use async_trait::async_trait; -use dioxus_core::ScopeState; +use dioxus_core::prelude::consume_context; +use dioxus_core::prelude::provide_context; use dioxus_html::prelude::{EvalError, EvalProvider, Evaluator}; use std::{cell::RefCell, rc::Rc}; use crate::{query::Query, DesktopContext}; /// Provides the DesktopEvalProvider through [`cx.provide_context`]. -pub fn init_eval(cx: &ScopeState) { - let desktop_ctx = cx.consume_context::().unwrap(); +pub fn init_eval() { + let desktop_ctx = consume_context::().unwrap(); let provider: Rc = Rc::new(DesktopEvalProvider { desktop_ctx }); - cx.provide_context(provider); + provide_context(provider); } /// Reprents the desktop-target's provider of evaluators. diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 9460ca87d..cc0af9469 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -126,7 +126,7 @@ pub fn launch_cfg(root: Component, config_builder: Config) { /// }) /// } /// ``` -pub fn launch_with_props(root: Component

, props: P, cfg: Config) { +pub fn launch_with_props(root: Component

, props: P, cfg: Config) { let event_loop = EventLoopBuilder::::with_user_event().build(); let proxy = event_loop.create_proxy(); @@ -301,13 +301,13 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) // check for a mounted event placeholder and replace it with a desktop specific element let as_any = if let dioxus_html::EventData::Mounted = &data { - let query = view - .dom - .base_scope() - .consume_context::() - .unwrap() - .query - .clone(); + let query = view.dom.in_runtime(|| { + ScopeId::ROOT + .consume_context::() + .unwrap() + .query + .clone() + }); let element = DesktopElement::new(element, view.desktop_context.clone(), query); @@ -319,7 +319,9 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) view.dom.handle_event(&name, as_any, element, bubbles); - send_edits(view.dom.render_immediate(), &view.desktop_context); + view.dom + .render_immediate(&mut *view.desktop_context.mutation_state.borrow_mut()); + send_edits(&view.desktop_context); } // When the webview sends a query, we need to send it to the query manager which handles dispatching the data to the correct pending query @@ -328,13 +330,13 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) if let Ok(result) = serde_json::from_value::(params) { let view = webviews.get(&event.1).unwrap(); - let query = view - .dom - .base_scope() - .consume_context::() - .unwrap() - .query - .clone(); + let query = view.dom.in_runtime(|| { + ScopeId::ROOT + .consume_context::() + .unwrap() + .query + .clone() + }); query.send(result); } @@ -342,7 +344,9 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) EventData::Ipc(msg) if msg.method() == "initialize" => { let view = webviews.get_mut(&event.1).unwrap(); - send_edits(view.dom.rebuild(), &view.desktop_context); + view.dom + .rebuild(&mut *view.desktop_context.mutation_state.borrow_mut()); + send_edits(&view.desktop_context); view.desktop_context .webview .window() @@ -384,7 +388,10 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) view.dom.handle_event(event_name, data, id, event_bubbles); } - send_edits(view.dom.render_immediate(), &view.desktop_context); + view.dom.render_immediate( + &mut *view.desktop_context.mutation_state.borrow_mut(), + ); + send_edits(&view.desktop_context); } } @@ -417,11 +424,12 @@ fn create_new_window( asset_handlers, )); - let cx = dom.base_scope(); - cx.provide_context(desktop_context.clone()); - - // Init eval - init_eval(cx); + let query = dom.in_runtime(|| { + let query = ScopeId::ROOT.provide_context(desktop_context.clone()); + // Init eval + init_eval(); + query + }); WebviewHandler { // We want to poll the virtualdom and the event loop at the same time, so the waker will be connected to both @@ -461,149 +469,17 @@ fn poll_vdom(view: &mut WebviewHandler) { } } - send_edits(view.dom.render_immediate(), &view.desktop_context); + view.dom + .render_immediate(&mut *view.desktop_context.mutation_state.borrow_mut()); + send_edits(&view.desktop_context); } } /// Send a list of mutations to the webview -fn send_edits(edits: Mutations, desktop_context: &DesktopContext) { - let mut channel = desktop_context.channel.borrow_mut(); - let mut templates = desktop_context.templates.borrow_mut(); - if let Some(bytes) = apply_edits( - edits, - &mut channel, - &mut templates, - &desktop_context.max_template_count, - ) { - desktop_context.edit_queue.add_edits(bytes) - } -} - -fn apply_edits( - mutations: Mutations, - channel: &mut Channel, - templates: &mut FxHashMap, - max_template_count: &AtomicU16, -) -> Option> { - use dioxus_core::Mutation::*; - if mutations.templates.is_empty() && mutations.edits.is_empty() { - return None; - } - for template in mutations.templates { - add_template(&template, channel, templates, max_template_count); - } - for edit in mutations.edits { - match edit { - AppendChildren { id, m } => channel.append_children(id.0 as u32, m as u16), - AssignId { path, id } => channel.assign_id(path, id.0 as u32), - CreatePlaceholder { id } => channel.create_placeholder(id.0 as u32), - CreateTextNode { value, id } => channel.create_text_node(value, id.0 as u32), - HydrateText { path, value, id } => channel.hydrate_text(path, value, id.0 as u32), - LoadTemplate { name, index, id } => { - if let Some(tmpl_id) = templates.get(name) { - channel.load_template(*tmpl_id, index as u16, id.0 as u32) - } - } - ReplaceWith { id, m } => channel.replace_with(id.0 as u32, m as u16), - ReplacePlaceholder { path, m } => channel.replace_placeholder(path, m as u16), - InsertAfter { id, m } => channel.insert_after(id.0 as u32, m as u16), - InsertBefore { id, m } => channel.insert_before(id.0 as u32, m as u16), - SetAttribute { - name, - value, - id, - ns, - } => match value { - BorrowedAttributeValue::Text(txt) => { - channel.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default()) - } - BorrowedAttributeValue::Float(f) => { - channel.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default()) - } - BorrowedAttributeValue::Int(n) => { - channel.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default()) - } - BorrowedAttributeValue::Bool(b) => channel.set_attribute( - id.0 as u32, - name, - if b { "true" } else { "false" }, - ns.unwrap_or_default(), - ), - BorrowedAttributeValue::None => { - channel.remove_attribute(id.0 as u32, name, ns.unwrap_or_default()) - } - _ => unreachable!(), - }, - SetText { value, id } => channel.set_text(id.0 as u32, value), - NewEventListener { name, id, .. } => { - channel.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8) - } - RemoveEventListener { name, id } => { - channel.remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8) - } - Remove { id } => channel.remove(id.0 as u32), - PushRoot { id } => channel.push_root(id.0 as u32), - } - } - - let bytes: Vec<_> = channel.export_memory().collect(); - channel.reset(); - Some(bytes) -} - -fn add_template( - template: &Template<'static>, - channel: &mut Channel, - templates: &mut FxHashMap, - max_template_count: &AtomicU16, -) { - let current_max_template_count = max_template_count.load(std::sync::atomic::Ordering::Relaxed); - for root in template.roots.iter() { - create_template_node(channel, root); - templates.insert(template.name.to_owned(), current_max_template_count); - } - channel.add_templates(current_max_template_count, template.roots.len() as u16); - - max_template_count.fetch_add(1, std::sync::atomic::Ordering::Relaxed); -} - -fn create_template_node(channel: &mut Channel, v: &'static TemplateNode<'static>) { - use TemplateNode::*; - match v { - Element { - tag, - namespace, - attrs, - children, - .. - } => { - // Push the current node onto the stack - match namespace { - Some(ns) => channel.create_element_ns(tag, ns), - None => channel.create_element(tag), - } - // Set attributes on the current node - for attr in *attrs { - if let TemplateAttribute::Static { - name, - value, - namespace, - } = attr - { - channel.set_top_attribute(name, value, namespace.unwrap_or_default()) - } - } - // Add each child to the stack - for child in *children { - create_template_node(channel, child); - } - // Add all children to the parent - channel.append_children_to_top(children.len() as u16); - } - Text { text } => channel.create_raw_text(text), - DynamicText { .. } => channel.create_raw_text("p"), - Dynamic { .. } => channel.add_placeholder(), - } +fn send_edits(desktop_context: &DesktopContext) { + let mut mutations = desktop_context.mutation_state.borrow_mut(); + let serialized_edits = mutations.export_memory(); + desktop_context.edit_queue.add_edits(serialized_edits); } /// Different hide implementations per platform diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 61c6af661..b373623d7 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -1,5 +1,5 @@ use crate::{window, DesktopContext}; -use dioxus_core::ScopeState; +use dioxus_core::prelude::{once, spawn}; use dioxus_interpreter_js::INTERPRETER_JS; use slab::Slab; use std::{ @@ -159,6 +159,7 @@ impl AssetHandlerRegistry { } /// A handle to a registered asset handler. +#[derive(Clone)] pub struct AssetHandlerHandle { desktop: DesktopContext, handler_id: Rc>, @@ -192,16 +193,13 @@ impl Drop for AssetHandlerHandle { /// /// The callback takes a path as requested by the web view, and it should return `Some(response)` /// if you want to load the asset, and `None` if you want to fallback on the default behavior. -pub fn use_asset_handler( - cx: &ScopeState, - handler: impl AssetHandler, -) -> &AssetHandlerHandle { - cx.use_hook(|| { +pub fn use_asset_handler(handler: impl AssetHandler) -> AssetHandlerHandle { + once(|| { let desktop = window(); let handler_id = Rc::new(OnceCell::new()); let handler_id_ref = Rc::clone(&handler_id); let desktop_ref = Rc::clone(&desktop); - cx.push_future(async move { + spawn(async move { let id = desktop.asset_handlers.register_handler(handler).await; handler_id.set(id).unwrap(); }); diff --git a/packages/desktop/src/shortcut.rs b/packages/desktop/src/shortcut.rs index bf8ed1382..4d292834e 100644 --- a/packages/desktop/src/shortcut.rs +++ b/packages/desktop/src/shortcut.rs @@ -1,6 +1,5 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc, str::FromStr}; -use dioxus_core::ScopeState; use dioxus_html::input_data::keyboard_types::Modifiers; use slab::Slab; use wry::application::keyboard::ModifiersState; @@ -100,7 +99,7 @@ impl ShortcutRegistry { Err(HotkeyError::HotKeyParseError(shortcut)) => { return Err(ShortcutRegistryError::InvalidShortcut(shortcut)) } - Err(err) => return Err(ShortcutRegistryError::Other(Box::new(err))), + Err(err) => return Err(ShortcutRegistryError::Other(Rc::new(err))), } }, ) @@ -125,24 +124,25 @@ impl ShortcutRegistry { } } -#[non_exhaustive] -#[derive(Debug)] /// An error that can occur when registering a shortcut. +#[non_exhaustive] +#[derive(Debug, Clone)] pub enum ShortcutRegistryError { /// The shortcut is invalid. InvalidShortcut(String), /// An unknown error occurred. - Other(Box), + Other(Rc), } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] /// An global id for a shortcut. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ShortcutId { id: u32, number: usize, } /// A global shortcut. This will be automatically removed when it is dropped. +#[derive(Clone)] pub struct ShortcutHandle { desktop: DesktopContext, /// The id of the shortcut @@ -179,11 +179,10 @@ impl IntoAccelerator for &str { /// Get a closure that executes any JavaScript in the WebView context. pub fn use_global_shortcut( - cx: &ScopeState, accelerator: impl IntoAccelerator, handler: impl FnMut() + 'static, -) -> &Result { - cx.use_hook(move || { +) -> Result { + dioxus_core::once(move || { let desktop = window(); let id = desktop.create_shortcut(accelerator.accelerator(), handler); diff --git a/packages/fullstack/src/hooks/server_future.rs b/packages/fullstack/src/hooks/server_future.rs index f8eaef963..0cf22c815 100644 --- a/packages/fullstack/src/hooks/server_future.rs +++ b/packages/fullstack/src/hooks/server_future.rs @@ -112,7 +112,7 @@ where pub struct UseServerFuture { update: Arc, needs_regen: Cell, - task: Cell>, + task: Cell>, dependencies: Vec>, value: Rc>>>, } @@ -142,7 +142,7 @@ impl UseServerFuture { } /// Get the ID of the future in Dioxus' internal scheduler - pub fn task(&self) -> Option { + pub fn task(&self) -> Option { self.task.get() } diff --git a/packages/hooks/src/use_coroutine.rs b/packages/hooks/src/use_coroutine.rs index f2577b90c..4fad17976 100644 --- a/packages/hooks/src/use_coroutine.rs +++ b/packages/hooks/src/use_coroutine.rs @@ -1,4 +1,4 @@ -use dioxus_core::{ScopeState, TaskId}; +use dioxus_core::{ScopeState, Task}; pub use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use std::future::Future; @@ -87,7 +87,7 @@ pub fn use_coroutine_handle(cx: &ScopeState) -> Option<&Coroutine pub struct Coroutine { tx: UnboundedSender, - task: TaskId, + task: Task, } // for use in futures @@ -103,7 +103,7 @@ impl Clone for Coroutine { impl Coroutine { /// Get the ID of this coroutine #[must_use] - pub fn task_id(&self) -> TaskId { + pub fn task_id(&self) -> Task { self.task } diff --git a/packages/hooks/src/use_effect.rs b/packages/hooks/src/use_effect.rs index 72744e8ae..8fd65c763 100644 --- a/packages/hooks/src/use_effect.rs +++ b/packages/hooks/src/use_effect.rs @@ -1,4 +1,4 @@ -use dioxus_core::{ScopeState, TaskId}; +use dioxus_core::{ScopeState, Task}; use std::{ any::Any, cell::{Cell, RefCell}, @@ -67,7 +67,7 @@ where { struct UseEffect { needs_regen: bool, - task: Cell>, + task: Cell>, dependencies: Vec>, cleanup: UseEffectCleanup, } @@ -108,14 +108,14 @@ type UseEffectCleanup = Rc>>>; /// Something that can be returned from a `use_effect` hook. pub trait UseEffectReturn { - fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> TaskId; + fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> Task; } impl UseEffectReturn<()> for T where T: Future + 'static, { - fn apply(self, _: UseEffectCleanup, cx: &ScopeState) -> TaskId { + fn apply(self, _: UseEffectCleanup, cx: &ScopeState) -> Task { cx.push_future(self) } } @@ -127,7 +127,7 @@ where T: Future + 'static, F: FnOnce() + 'static, { - fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> TaskId { + fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> Task { cx.push_future(async move { let cleanup = self.await; *oncleanup.borrow_mut() = Some(Box::new(cleanup) as Box); diff --git a/packages/hooks/src/use_future.rs b/packages/hooks/src/use_future.rs index b9aa20c31..bb118d2a4 100644 --- a/packages/hooks/src/use_future.rs +++ b/packages/hooks/src/use_future.rs @@ -1,5 +1,5 @@ #![allow(missing_docs)] -use dioxus_core::{ScopeState, TaskId}; +use dioxus_core::{ScopeState, Task}; use std::{any::Any, cell::Cell, future::Future, rc::Rc, sync::Arc}; use crate::{use_state, UseState}; @@ -74,7 +74,7 @@ pub enum FutureState<'a, T> { pub struct UseFuture { update: Arc, needs_regen: Rc>, - task: Rc>>, + task: Rc>>, state: UseState>, } @@ -114,7 +114,7 @@ impl UseFuture { } /// Get the ID of the future in Dioxus' internal scheduler - pub fn task(&self) -> Option { + pub fn task(&self) -> Option { self.task.get() } diff --git a/packages/hot-reload/src/lib.rs b/packages/hot-reload/src/lib.rs index 46efb8341..44e64b7a9 100644 --- a/packages/hot-reload/src/lib.rs +++ b/packages/hot-reload/src/lib.rs @@ -16,10 +16,10 @@ pub use file_watcher::*; /// A message the hot reloading server sends to the client #[derive(Debug, Serialize, Deserialize, Clone, Copy)] +#[serde(bound(deserialize = "'de: 'static"))] pub enum HotReloadMsg { /// A template has been updated - #[serde(borrow = "'static")] - UpdateTemplate(Template<'static>), + UpdateTemplate(Template), /// The program needs to be recompiled, and the client should shut down Shutdown, } diff --git a/packages/html/src/eval.rs b/packages/html/src/eval.rs index 394713af6..7d5484dca 100644 --- a/packages/html/src/eval.rs +++ b/packages/html/src/eval.rs @@ -1,7 +1,7 @@ #![allow(clippy::await_holding_refcell_ref)] use async_trait::async_trait; -use dioxus_core::ScopeState; +use dioxus_core::prelude::*; use std::future::{Future, IntoFuture}; use std::pin::Pin; use std::rc::Rc; @@ -34,18 +34,14 @@ type EvalCreator = Rc Result>; /// it. **This applies especially to web targets, where the JavaScript context /// has access to most, if not all of your application data.** #[must_use] -pub fn use_eval(cx: &ScopeState) -> &EvalCreator { - &*cx.use_hook(|| { - let eval_provider = cx - .consume_context::>() - .expect("evaluator not provided"); +pub fn eval_provider() -> EvalCreator { + let eval_provider = consume_context::>().expect("evaluator not provided"); - Rc::new(move |script: &str| { - eval_provider - .new_evaluator(script.to_string()) - .map(UseEval::new) - }) as Rc Result> - }) + Rc::new(move |script: &str| { + eval_provider + .new_evaluator(script.to_string()) + .map(UseEval::new) + }) as Rc Result> } pub fn eval(script: &str) -> Result { diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index 32c48aafb..af1ea049e 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -9,11 +9,11 @@ macro_rules! impl_event { $( $( #[$attr] )* #[inline] - pub fn $name<'a, E: crate::EventReturn, T>(_cx: &'a ::dioxus_core::ScopeState, mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'a) -> ::dioxus_core::Attribute { + pub fn $name, T>(mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'static) -> ::dioxus_core::Attribute { ::dioxus_core::Attribute::new( stringify!($name), - _cx.listener(move |e: ::dioxus_core::Event<$data>| { - _f(e).spawn(_cx); + ::dioxus_core::AttributeValue::listener(move |e: ::dioxus_core::Event<$data>| { + _f(e).spawn(); }), None, false, @@ -155,7 +155,7 @@ use std::future::Future; #[doc(hidden)] pub trait EventReturn

: Sized { - fn spawn(self, _cx: &dioxus_core::ScopeState) {} + fn spawn(self) {} } impl EventReturn<()> for () {} @@ -167,7 +167,7 @@ where T: Future + 'static, { #[inline] - fn spawn(self, cx: &dioxus_core::ScopeState) { - cx.spawn(self); + fn spawn(self) { + dioxus_core::prelude::spawn(self); } } diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index 890d56546..349d36905 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -148,14 +148,13 @@ impl_event! { /// ondoubleclick #[inline] -pub fn ondoubleclick<'a, E: crate::EventReturn, T>( - _cx: &'a ::dioxus_core::ScopeState, - mut _f: impl FnMut(::dioxus_core::Event) -> E + 'a, +pub fn ondoubleclick, T>( + mut _f: impl FnMut(::dioxus_core::Event) -> E + 'static, ) -> ::dioxus_core::Attribute { ::dioxus_core::Attribute::new( "ondblclick", - _cx.listener(move |e: ::dioxus_core::Event| { - _f(e).spawn(_cx); + dioxus_core::AttributeValue::listener(move |e: ::dioxus_core::Event| { + _f(e).spawn(); }), None, false, diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs index a8fb367d0..007ced313 100644 --- a/packages/router/examples/simple_routes.rs +++ b/packages/router/examples/simple_routes.rs @@ -122,7 +122,7 @@ fn Route3(cx: Scope, dynamic: String) -> Element { .collect::>(); let navigator = use_navigator(cx); - + render! { input { oninput: move |evt| *current_route_str.write() = evt.value.clone(),