Better expect error messages (#2629)
* Replace expect error messages * Change message * Create RuntimeError struct * Pass error through core methods * Fix use of Runtime::current in signals package * Fix tests * Add #[track_caller] for better error output and fix maybe_with_rt * provide a help message along with RuntimeError --------- Co-authored-by: Evan Almloff <evanalmloff@gmail.com>
This commit is contained in:
1 parent
9167cd9dec
commit
d07e81005f
12 files changed
+165
-63
No files matched your search
@@ -167,7 +167,7 @@ where
|
|||||||
error.context.push(Rc::new(AdditionalErrorContext {
|
error.context.push(Rc::new(AdditionalErrorContext {
|
||||||
backtrace: Backtrace::capture(),
|
backtrace: Backtrace::capture(),
|
||||||
context: Box::new(context()),
|
context: Box::new(context()),
|
||||||
scope: current_scope_id(),
|
scope: current_scope_id().ok(),
|
||||||
}));
|
}));
|
||||||
Err(error)
|
Err(error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -397,7 +397,7 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
|
|||||||
));
|
));
|
||||||
Self {
|
Self {
|
||||||
callback,
|
callback,
|
||||||
origin: current_scope_id().expect("to be in a dioxus runtime"),
|
origin: current_scope_id().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,7 +409,7 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
|
|||||||
as Rc<RefCell<dyn FnMut(Args) -> Ret>>));
|
as Rc<RefCell<dyn FnMut(Args) -> Ret>>));
|
||||||
Self {
|
Self {
|
||||||
callback,
|
callback,
|
||||||
origin: current_scope_id().expect("to be in a dioxus runtime"),
|
origin: current_scope_id().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,7 +427,7 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
|
|||||||
value
|
value
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.expect("Callback must be called from within the dioxus runtime")
|
.unwrap()
|
||||||
} else {
|
} else {
|
||||||
panic!("Callback was manually dropped")
|
panic!("Callback was manually dropped")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,6 @@ pub fn current_owner<S: AnyStorage>() -> Owner<S> {
|
|||||||
impl ScopeId {
|
impl ScopeId {
|
||||||
/// Get the owner for the current scope.
|
/// Get the owner for the current scope.
|
||||||
pub fn owner<S: AnyStorage>(self) -> Owner<S> {
|
pub fn owner<S: AnyStorage>(self) -> Owner<S> {
|
||||||
Runtime::with_scope(self, |cx| cx.owner::<S>()).expect("to be in a dioxus runtime")
|
Runtime::with_scope(self, |cx| cx.owner::<S>()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
|
use crate::runtime::RuntimeError;
|
||||||
use crate::{innerlude::SuspendedFuture, runtime::Runtime, CapturedError, Element, ScopeId, Task};
|
use crate::{innerlude::SuspendedFuture, runtime::Runtime, CapturedError, Element, ScopeId, Task};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Get the current scope id
|
/// Get the current scope id
|
||||||
pub fn current_scope_id() -> Option<ScopeId> {
|
pub fn current_scope_id() -> Result<ScopeId, RuntimeError> {
|
||||||
Runtime::with(|rt| rt.current_scope_id()).flatten()
|
Runtime::with(|rt| rt.current_scope_id().ok())
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.ok_or(RuntimeError::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@@ -31,19 +35,20 @@ pub fn vdom_is_rendering() -> bool {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn throw_error(error: impl Into<CapturedError> + 'static) {
|
pub fn throw_error(error: impl Into<CapturedError> + 'static) {
|
||||||
current_scope_id()
|
current_scope_id().unwrap().throw_error(error)
|
||||||
.expect("to be in a dioxus runtime")
|
|
||||||
.throw_error(error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume context from the current scope
|
/// Consume context from the current scope
|
||||||
pub fn try_consume_context<T: 'static + Clone>() -> Option<T> {
|
pub fn try_consume_context<T: 'static + Clone>() -> Option<T> {
|
||||||
Runtime::with_current_scope(|cx| cx.consume_context::<T>()).flatten()
|
Runtime::with_current_scope(|cx| cx.consume_context::<T>())
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume context from the current scope
|
/// Consume context from the current scope
|
||||||
pub fn consume_context<T: 'static + Clone>() -> T {
|
pub fn consume_context<T: 'static + Clone>() -> T {
|
||||||
Runtime::with_current_scope(|cx| cx.consume_context::<T>())
|
Runtime::with_current_scope(|cx| cx.consume_context::<T>())
|
||||||
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
.unwrap_or_else(|| panic!("Could not find context {}", std::any::type_name::<T>()))
|
.unwrap_or_else(|| panic!("Could not find context {}", std::any::type_name::<T>()))
|
||||||
}
|
}
|
||||||
@@ -54,23 +59,25 @@ pub fn consume_context_from_scope<T: 'static + Clone>(scope_id: ScopeId) -> Opti
|
|||||||
rt.get_state(scope_id)
|
rt.get_state(scope_id)
|
||||||
.and_then(|cx| cx.consume_context::<T>())
|
.and_then(|cx| cx.consume_context::<T>())
|
||||||
})
|
})
|
||||||
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the current scope has a context
|
/// Check if the current scope has a context
|
||||||
pub fn has_context<T: 'static + Clone>() -> Option<T> {
|
pub fn has_context<T: 'static + Clone>() -> Option<T> {
|
||||||
Runtime::with_current_scope(|cx| cx.has_context::<T>()).flatten()
|
Runtime::with_current_scope(|cx| cx.has_context::<T>())
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide context to the current scope
|
/// Provide context to the current scope
|
||||||
pub fn provide_context<T: 'static + Clone>(value: T) -> T {
|
pub fn provide_context<T: 'static + Clone>(value: T) -> T {
|
||||||
Runtime::with_current_scope(|cx| cx.provide_context(value)).expect("to be in a dioxus runtime")
|
Runtime::with_current_scope(|cx| cx.provide_context(value)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide a context to the root scope
|
/// Provide a context to the root scope
|
||||||
pub fn provide_root_context<T: 'static + Clone>(value: T) -> T {
|
pub fn provide_root_context<T: 'static + Clone>(value: T) -> T {
|
||||||
Runtime::with_current_scope(|cx| cx.provide_root_context(value))
|
Runtime::with_current_scope(|cx| cx.provide_root_context(value)).unwrap()
|
||||||
.expect("to be in a dioxus runtime")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Suspended the current component on a specific task and then return None
|
/// Suspended the current component on a specific task and then return None
|
||||||
@@ -108,7 +115,7 @@ pub fn suspend(task: Task) -> Element {
|
|||||||
///
|
///
|
||||||
#[doc = include_str!("../docs/common_spawn_errors.md")]
|
#[doc = include_str!("../docs/common_spawn_errors.md")]
|
||||||
pub fn spawn_isomorphic(fut: impl Future<Output = ()> + 'static) -> Task {
|
pub fn spawn_isomorphic(fut: impl Future<Output = ()> + 'static) -> Task {
|
||||||
Runtime::with_current_scope(|cx| cx.spawn_isomorphic(fut)).expect("to be in a dioxus runtime")
|
Runtime::with_current_scope(|cx| cx.spawn_isomorphic(fut)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns the future but does not return the [`Task`]. This task will automatically be canceled when the component is dropped.
|
/// Spawns the future but does not return the [`Task`]. This task will automatically be canceled when the component is dropped.
|
||||||
@@ -134,12 +141,12 @@ pub fn spawn_isomorphic(fut: impl Future<Output = ()> + 'static) -> Task {
|
|||||||
///
|
///
|
||||||
#[doc = include_str!("../docs/common_spawn_errors.md")]
|
#[doc = include_str!("../docs/common_spawn_errors.md")]
|
||||||
pub fn spawn(fut: impl Future<Output = ()> + 'static) -> Task {
|
pub fn spawn(fut: impl Future<Output = ()> + 'static) -> Task {
|
||||||
Runtime::with_current_scope(|cx| cx.spawn(fut)).expect("to be in a dioxus runtime")
|
Runtime::with_current_scope(|cx| cx.spawn(fut)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queue an effect to run after the next render. You generally shouldn't need to interact with this function directly. [use_effect](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) will call this function for you.
|
/// Queue an effect to run after the next render. You generally shouldn't need to interact with this function directly. [use_effect](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) will call this function for you.
|
||||||
pub fn queue_effect(f: impl FnOnce() + 'static) {
|
pub fn queue_effect(f: impl FnOnce() + 'static) {
|
||||||
Runtime::with_current_scope(|cx| cx.queue_effect(f)).expect("to be in a dioxus runtime")
|
Runtime::with_current_scope(|cx| cx.queue_effect(f)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn a future that Dioxus won't clean up when this component is unmounted
|
/// Spawn a future that Dioxus won't clean up when this component is unmounted
|
||||||
@@ -195,7 +202,7 @@ pub fn queue_effect(f: impl FnOnce() + 'static) {
|
|||||||
///
|
///
|
||||||
#[doc = include_str!("../docs/common_spawn_errors.md")]
|
#[doc = include_str!("../docs/common_spawn_errors.md")]
|
||||||
pub fn spawn_forever(fut: impl Future<Output = ()> + 'static) -> Option<Task> {
|
pub fn spawn_forever(fut: impl Future<Output = ()> + 'static) -> Option<Task> {
|
||||||
Runtime::with_scope(ScopeId::ROOT, |cx| cx.spawn(fut))
|
Runtime::with_scope(ScopeId::ROOT, |cx| cx.spawn(fut)).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Informs the scheduler that this task is no longer needed and should be removed.
|
/// Informs the scheduler that this task is no longer needed and should be removed.
|
||||||
@@ -248,30 +255,33 @@ pub fn remove_future(id: Task) {
|
|||||||
/// })
|
/// })
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[track_caller]
|
||||||
pub fn use_hook<State: Clone + 'static>(initializer: impl FnOnce() -> State) -> State {
|
pub fn use_hook<State: Clone + 'static>(initializer: impl FnOnce() -> State) -> State {
|
||||||
Runtime::with_current_scope(|cx| cx.use_hook(initializer)).expect("to be in a dioxus runtime")
|
Runtime::with_current_scope(|cx| cx.use_hook(initializer)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current render since the inception of this component
|
/// Get the current render since the inception of this component
|
||||||
///
|
///
|
||||||
/// This can be used as a helpful diagnostic when debugging hooks/renders, etc
|
/// This can be used as a helpful diagnostic when debugging hooks/renders, etc
|
||||||
pub fn generation() -> usize {
|
pub fn generation() -> usize {
|
||||||
Runtime::with_current_scope(|cx| cx.generation()).expect("to be in a dioxus runtime")
|
Runtime::with_current_scope(|cx| cx.generation()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the parent of the current scope if it exists
|
/// Get the parent of the current scope if it exists
|
||||||
pub fn parent_scope() -> Option<ScopeId> {
|
pub fn parent_scope() -> Option<ScopeId> {
|
||||||
Runtime::with_current_scope(|cx| cx.parent_id()).flatten()
|
Runtime::with_current_scope(|cx| cx.parent_id())
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark the current scope as dirty, causing it to re-render
|
/// Mark the current scope as dirty, causing it to re-render
|
||||||
pub fn needs_update() {
|
pub fn needs_update() {
|
||||||
Runtime::with_current_scope(|cx| cx.needs_update());
|
let _ = Runtime::with_current_scope(|cx| cx.needs_update());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark the current scope as dirty, causing it to re-render
|
/// Mark the current scope as dirty, causing it to re-render
|
||||||
pub fn needs_update_any(id: ScopeId) {
|
pub fn needs_update_any(id: ScopeId) {
|
||||||
Runtime::with_current_scope(|cx| cx.needs_update_any(id));
|
let _ = Runtime::with_current_scope(|cx| cx.needs_update_any(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule an update for the current component
|
/// Schedule an update for the current component
|
||||||
@@ -280,7 +290,7 @@ pub fn needs_update_any(id: ScopeId) {
|
|||||||
///
|
///
|
||||||
/// You should prefer [`schedule_update_any`] if you need to update multiple components.
|
/// You should prefer [`schedule_update_any`] if you need to update multiple components.
|
||||||
pub fn schedule_update() -> Arc<dyn Fn() + Send + Sync> {
|
pub fn schedule_update() -> Arc<dyn Fn() + Send + Sync> {
|
||||||
Runtime::with_current_scope(|cx| cx.schedule_update()).expect("to be in a dioxus runtime")
|
Runtime::with_current_scope(|cx| cx.schedule_update()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule an update for any component given its [`ScopeId`].
|
/// Schedule an update for any component given its [`ScopeId`].
|
||||||
@@ -289,7 +299,7 @@ pub fn schedule_update() -> Arc<dyn Fn() + Send + Sync> {
|
|||||||
///
|
///
|
||||||
/// Note: Unlike [`needs_update`], the function returned by this method will work outside of the dioxus runtime.
|
/// Note: Unlike [`needs_update`], the function returned by this method will work outside of the dioxus runtime.
|
||||||
pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
|
pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
|
||||||
Runtime::with_current_scope(|cx| cx.schedule_update_any()).expect("to be in a dioxus runtime")
|
Runtime::with_current_scope(|cx| cx.schedule_update_any()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a callback that will be run before the component is removed.
|
/// Creates a callback that will be run before the component is removed.
|
||||||
@@ -393,12 +403,12 @@ pub fn use_after_render(f: impl FnMut() + 'static) {
|
|||||||
/// This is a hook and will always run, so you can't unschedule it
|
/// This is a hook and will always run, so you can't unschedule it
|
||||||
/// Will run for every progression of suspense, though this might change in the future
|
/// Will run for every progression of suspense, though this might change in the future
|
||||||
pub fn before_render(f: impl FnMut() + 'static) {
|
pub fn before_render(f: impl FnMut() + 'static) {
|
||||||
Runtime::with_current_scope(|cx| cx.push_before_render(f));
|
let _ = Runtime::with_current_scope(|cx| cx.push_before_render(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a function to be run after the render is complete, even if it didn't complete successfully
|
/// Push a function to be run after the render is complete, even if it didn't complete successfully
|
||||||
pub fn after_render(f: impl FnMut() + 'static) {
|
pub fn after_render(f: impl FnMut() + 'static) {
|
||||||
Runtime::with_current_scope(|cx| cx.push_after_render(f));
|
let _ = Runtime::with_current_scope(|cx| cx.push_after_render(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use a hook with a cleanup function
|
/// Use a hook with a cleanup function
|
||||||
|
|||||||
@@ -130,9 +130,11 @@ pub fn verify_component_called_as_component<C: ComponentFunction<P, M>, P, M>(co
|
|||||||
}
|
}
|
||||||
let component_name = Runtime::with(|rt| {
|
let component_name = Runtime::with(|rt| {
|
||||||
current_scope_id()
|
current_scope_id()
|
||||||
|
.ok()
|
||||||
.and_then(|id| rt.get_state(id))
|
.and_then(|id| rt.get_state(id))
|
||||||
.map(|scope| scope.name)
|
.map(|scope| scope.name)
|
||||||
})
|
})
|
||||||
|
.ok()
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
// If we are in a component, and the type name is the same as the active component name, then we can just return
|
// If we are in a component, and the type name is the same as the active component name, then we can just return
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use slotmap::DefaultKey;
|
use slotmap::DefaultKey;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
use std::fmt;
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Cell, Ref, RefCell},
|
cell::{Cell, Ref, RefCell},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
@@ -66,8 +67,10 @@ impl Runtime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current runtime
|
/// Get the current runtime
|
||||||
pub fn current() -> Option<Rc<Self>> {
|
pub fn current() -> Result<Rc<Self>, RuntimeError> {
|
||||||
RUNTIMES.with(|stack| stack.borrow().last().cloned())
|
RUNTIMES
|
||||||
|
.with(|stack| stack.borrow().last().cloned())
|
||||||
|
.ok_or(RuntimeError::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a scope context. This slab is synchronized with the scope slab.
|
/// Create a scope context. This slab is synchronized with the scope slab.
|
||||||
@@ -106,8 +109,12 @@ impl Runtime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current scope id
|
/// Get the current scope id
|
||||||
pub(crate) fn current_scope_id(&self) -> Option<ScopeId> {
|
pub(crate) fn current_scope_id(&self) -> Result<ScopeId, RuntimeError> {
|
||||||
self.scope_stack.borrow().last().copied()
|
self.scope_stack
|
||||||
|
.borrow()
|
||||||
|
.last()
|
||||||
|
.copied()
|
||||||
|
.ok_or(RuntimeError { _priv: () })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call this function with the current scope set to the given scope
|
/// Call this function with the current scope set to the given scope
|
||||||
@@ -190,22 +197,31 @@ impl Runtime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Runs a function with the current runtime
|
/// Runs a function with the current runtime
|
||||||
pub(crate) fn with<R>(f: impl FnOnce(&Runtime) -> R) -> Option<R> {
|
pub(crate) fn with<R>(f: impl FnOnce(&Runtime) -> R) -> Result<R, RuntimeError> {
|
||||||
Self::current().map(|r| f(&r))
|
Self::current().map(|r| f(&r))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs a function with the current scope
|
/// Runs a function with the current scope
|
||||||
pub(crate) fn with_current_scope<R>(f: impl FnOnce(&Scope) -> R) -> Option<R> {
|
pub(crate) fn with_current_scope<R>(f: impl FnOnce(&Scope) -> R) -> Result<R, RuntimeError> {
|
||||||
Self::with(|rt| {
|
Self::with(|rt| {
|
||||||
rt.current_scope_id()
|
rt.current_scope_id()
|
||||||
|
.ok()
|
||||||
.and_then(|scope| rt.get_state(scope).map(|sc| f(&sc)))
|
.and_then(|scope| rt.get_state(scope).map(|sc| f(&sc)))
|
||||||
})
|
})
|
||||||
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
|
.ok_or(RuntimeError::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs a function with the current scope
|
/// Runs a function with the current scope
|
||||||
pub(crate) fn with_scope<R>(scope: ScopeId, f: impl FnOnce(&Scope) -> R) -> Option<R> {
|
pub(crate) fn with_scope<R>(
|
||||||
Self::with(|rt| rt.get_state(scope).map(|sc| f(&sc))).flatten()
|
scope: ScopeId,
|
||||||
|
f: impl FnOnce(&Scope) -> R,
|
||||||
|
) -> Result<R, RuntimeError> {
|
||||||
|
Self::with(|rt| rt.get_state(scope).map(|sc| f(&sc)))
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.ok_or(RuntimeError::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish a render. This will mark all effects as ready to run and send the render signal.
|
/// Finish a render. This will mark all effects as ready to run and send the render signal.
|
||||||
@@ -279,3 +295,61 @@ impl Drop for RuntimeGuard {
|
|||||||
Runtime::pop();
|
Runtime::pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Missing Dioxus runtime error.
|
||||||
|
pub struct RuntimeError {
|
||||||
|
_priv: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RuntimeError {
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self { _priv: () }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for RuntimeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("RuntimeError").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RuntimeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Must be called from inside a Dioxus runtime.
|
||||||
|
|
||||||
|
Help: Some APIs in dioxus require a global runtime to be present.
|
||||||
|
If you are calling one of these APIs from outside of a dioxus runtime
|
||||||
|
(typically in a web-sys closure or dynamic library), you will need to
|
||||||
|
grab the runtime from a scope that has it and then move it into your
|
||||||
|
new scope with a runtime guard.
|
||||||
|
|
||||||
|
For example, if you are trying to use dioxus apis from a web-sys
|
||||||
|
closure, you can grab the runtime from the scope it is created in:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
static COUNT: GlobalSignal<i32> = Signal::global(|| 0);
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn MyComponent() -> Element {
|
||||||
|
use_effect(|| {
|
||||||
|
// Grab the runtime from the MyComponent scope
|
||||||
|
let runtime = Runtime::current().expect(\"Components run in the Dioxus runtime\");
|
||||||
|
// Move the runtime into the web-sys closure scope
|
||||||
|
let web_sys_closure = Closure::new(|| {
|
||||||
|
// Then create a guard to provide the runtime to the closure
|
||||||
|
let _guard = RuntimeGuard::new(runtime);
|
||||||
|
// and run whatever code needs the runtime
|
||||||
|
tracing::info!(\"The count is: {COUNT}\");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for RuntimeError {}
|
||||||
@@ -16,7 +16,7 @@ impl VirtualDom {
|
|||||||
props: BoxedAnyProps,
|
props: BoxedAnyProps,
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
) -> &mut ScopeState {
|
) -> &mut ScopeState {
|
||||||
let parent_id = self.runtime.current_scope_id();
|
let parent_id = self.runtime.current_scope_id().ok();
|
||||||
let height = match parent_id.and_then(|id| self.runtime.get_state(id)) {
|
let height = match parent_id.and_then(|id| self.runtime.get_state(id)) {
|
||||||
Some(parent) => parent.height() + 1,
|
Some(parent) => parent.height() + 1,
|
||||||
None => 0,
|
None => 0,
|
||||||
@@ -47,11 +47,11 @@ impl VirtualDom {
|
|||||||
|
|
||||||
/// Run a scope and return the rendered nodes. This will not modify the DOM or update the last rendered node of the scope.
|
/// Run a scope and return the rendered nodes. This will not modify the DOM or update the last rendered node of the scope.
|
||||||
#[tracing::instrument(skip(self), level = "trace", name = "VirtualDom::run_scope")]
|
#[tracing::instrument(skip(self), level = "trace", name = "VirtualDom::run_scope")]
|
||||||
|
#[track_caller]
|
||||||
pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> RenderReturn {
|
pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> RenderReturn {
|
||||||
debug_assert!(
|
// Ensure we are currently inside a `Runtime`.
|
||||||
crate::Runtime::current().is_some(),
|
crate::Runtime::current().unwrap();
|
||||||
"Must be in a dioxus runtime"
|
|
||||||
);
|
|
||||||
self.runtime.clone().with_scope_on_stack(scope_id, || {
|
self.runtime.clone().with_scope_on_stack(scope_id, || {
|
||||||
let scope = &self.scopes[scope_id.0];
|
let scope = &self.scopes[scope_id.0];
|
||||||
let output = {
|
let output = {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::runtime::RuntimeError;
|
||||||
use crate::{innerlude::SchedulerMsg, Runtime, ScopeId, Task};
|
use crate::{innerlude::SchedulerMsg, Runtime, ScopeId, Task};
|
||||||
use crate::{
|
use crate::{
|
||||||
innerlude::{throw_into, CapturedError},
|
innerlude::{throw_into, CapturedError},
|
||||||
@@ -219,7 +220,7 @@ impl Scope {
|
|||||||
None
|
None
|
||||||
});
|
});
|
||||||
|
|
||||||
match cur_runtime.flatten() {
|
match cur_runtime.ok().flatten() {
|
||||||
Some(ctx) => Some(ctx),
|
Some(ctx) => Some(ctx),
|
||||||
None => {
|
None => {
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
@@ -450,13 +451,18 @@ impl Scope {
|
|||||||
|
|
||||||
impl ScopeId {
|
impl ScopeId {
|
||||||
/// Get the current scope id
|
/// Get the current scope id
|
||||||
pub fn current_scope_id(self) -> Option<ScopeId> {
|
pub fn current_scope_id(self) -> Result<ScopeId, RuntimeError> {
|
||||||
Runtime::with(|rt| rt.current_scope_id()).flatten()
|
Runtime::with(|rt| rt.current_scope_id().ok())
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.ok_or(RuntimeError::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume context from the current scope
|
/// Consume context from the current scope
|
||||||
pub fn consume_context<T: 'static + Clone>(self) -> Option<T> {
|
pub fn consume_context<T: 'static + Clone>(self) -> Option<T> {
|
||||||
Runtime::with_scope(self, |cx| cx.consume_context::<T>()).flatten()
|
Runtime::with_scope(self, |cx| cx.consume_context::<T>())
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume context from the current scope
|
/// Consume context from the current scope
|
||||||
@@ -465,64 +471,67 @@ impl ScopeId {
|
|||||||
rt.get_state(scope_id)
|
rt.get_state(scope_id)
|
||||||
.and_then(|cx| cx.consume_context::<T>())
|
.and_then(|cx| cx.consume_context::<T>())
|
||||||
})
|
})
|
||||||
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the current scope has a context
|
/// Check if the current scope has a context
|
||||||
pub fn has_context<T: 'static + Clone>(self) -> Option<T> {
|
pub fn has_context<T: 'static + Clone>(self) -> Option<T> {
|
||||||
Runtime::with_scope(self, |cx| cx.has_context::<T>()).flatten()
|
Runtime::with_scope(self, |cx| cx.has_context::<T>())
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide context to the current scope
|
/// Provide context to the current scope
|
||||||
pub fn provide_context<T: 'static + Clone>(self, value: T) -> T {
|
pub fn provide_context<T: 'static + Clone>(self, value: T) -> T {
|
||||||
Runtime::with_scope(self, |cx| cx.provide_context(value))
|
Runtime::with_scope(self, |cx| cx.provide_context(value)).unwrap()
|
||||||
.expect("to be in a dioxus runtime")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes the future onto the poll queue to be polled after the component renders.
|
/// Pushes the future onto the poll queue to be polled after the component renders.
|
||||||
pub fn push_future(self, fut: impl Future<Output = ()> + 'static) -> Option<Task> {
|
pub fn push_future(self, fut: impl Future<Output = ()> + 'static) -> Option<Task> {
|
||||||
Runtime::with_scope(self, |cx| cx.spawn(fut))
|
Runtime::with_scope(self, |cx| cx.spawn(fut)).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns the future but does not return the [`Task`]
|
/// Spawns the future but does not return the [`Task`]
|
||||||
pub fn spawn(self, fut: impl Future<Output = ()> + 'static) {
|
pub fn spawn(self, fut: impl Future<Output = ()> + 'static) {
|
||||||
Runtime::with_scope(self, |cx| cx.spawn(fut));
|
Runtime::with_scope(self, |cx| cx.spawn(fut)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current render since the inception of this component
|
/// Get the current render since the inception of this component
|
||||||
///
|
///
|
||||||
/// This can be used as a helpful diagnostic when debugging hooks/renders, etc
|
/// This can be used as a helpful diagnostic when debugging hooks/renders, etc
|
||||||
pub fn generation(self) -> Option<usize> {
|
pub fn generation(self) -> Option<usize> {
|
||||||
Runtime::with_scope(self, |cx| Some(cx.generation())).expect("to be in a dioxus runtime")
|
Runtime::with_scope(self, |cx| Some(cx.generation())).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the parent of the current scope if it exists
|
/// Get the parent of the current scope if it exists
|
||||||
pub fn parent_scope(self) -> Option<ScopeId> {
|
pub fn parent_scope(self) -> Option<ScopeId> {
|
||||||
Runtime::with_scope(self, |cx| cx.parent_id()).flatten()
|
Runtime::with_scope(self, |cx| cx.parent_id())
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark the current scope as dirty, causing it to re-render
|
/// Mark the current scope as dirty, causing it to re-render
|
||||||
pub fn needs_update(self) {
|
pub fn needs_update(self) {
|
||||||
Runtime::with_scope(self, |cx| cx.needs_update());
|
Runtime::with_scope(self, |cx| cx.needs_update()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a subscription that schedules a future render for the reference component. Unlike [`Self::needs_update`], this function will work outside of the dioxus runtime.
|
/// Create a subscription that schedules a future render for the reference component. Unlike [`Self::needs_update`], this function will work outside of the dioxus runtime.
|
||||||
///
|
///
|
||||||
/// ## Notice: you should prefer using [`crate::prelude::schedule_update_any`]
|
/// ## Notice: you should prefer using [`crate::prelude::schedule_update_any`]
|
||||||
pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
|
pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
|
||||||
Runtime::with_scope(*self, |cx| cx.schedule_update()).expect("to be in a dioxus runtime")
|
Runtime::with_scope(*self, |cx| cx.schedule_update()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the height of the current scope
|
/// Get the height of the current scope
|
||||||
pub fn height(self) -> u32 {
|
pub fn height(self) -> u32 {
|
||||||
Runtime::with_scope(self, |cx| cx.height()).expect("to be in a dioxus runtime")
|
Runtime::with_scope(self, |cx| cx.height()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a closure inside of scope's runtime
|
/// Run a closure inside of scope's runtime
|
||||||
|
#[track_caller]
|
||||||
pub fn in_runtime<T>(self, f: impl FnOnce() -> T) -> T {
|
pub fn in_runtime<T>(self, f: impl FnOnce() -> T) -> T {
|
||||||
Runtime::current()
|
Runtime::current().unwrap().on_scope(self, f)
|
||||||
.expect("to be in a dioxus runtime")
|
|
||||||
.on_scope(self, f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Throw a [`CapturedError`] into a scope. The error will bubble up to the nearest [`ErrorBoundary`] or the root of the app.
|
/// Throw a [`CapturedError`] into a scope. The error will bubble up to the nearest [`ErrorBoundary`] or the root of the app.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ impl std::fmt::Debug for ScopeId {
|
|||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
||||||
if let Some(name) = Runtime::current()
|
if let Some(name) = Runtime::current()
|
||||||
|
.ok()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|rt| rt.get_state(*self))
|
.and_then(|rt| rt.get_state(*self))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ impl SuspendedFuture {
|
|||||||
pub fn new(task: Task) -> Self {
|
pub fn new(task: Task) -> Self {
|
||||||
Self {
|
Self {
|
||||||
task,
|
task,
|
||||||
origin: current_scope_id().expect("to be in a dioxus runtime"),
|
origin: current_scope_id().unwrap(),
|
||||||
placeholder: VNode::placeholder(),
|
placeholder: VNode::placeholder(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ impl Task {
|
|||||||
_ = rt
|
_ = rt
|
||||||
.sender
|
.sender
|
||||||
.unbounded_send(SchedulerMsg::TaskNotified(self.id))
|
.unbounded_send(SchedulerMsg::TaskNotified(self.id))
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Poll the task immediately.
|
/// Poll the task immediately.
|
||||||
@@ -100,7 +101,8 @@ impl Task {
|
|||||||
.unbounded_send(SchedulerMsg::TaskNotified(self.id));
|
.unbounded_send(SchedulerMsg::TaskNotified(self.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +248,11 @@ impl Runtime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_task_wakeup(&self, id: Task) -> Poll<()> {
|
pub(crate) fn handle_task_wakeup(&self, id: Task) -> Poll<()> {
|
||||||
debug_assert!(Runtime::current().is_some(), "Must be in a dioxus runtime");
|
#[cfg(feature = "debug_assertions")]
|
||||||
|
{
|
||||||
|
// Ensure we are currently inside a `Runtime`.
|
||||||
|
Runtime::current().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
let task = self.tasks.borrow().get(id.id).cloned();
|
let task = self.tasks.borrow().get(id.id).cloned();
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ impl<T: 'static> GlobalSignal<T> {
|
|||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn maybe_with_rt<O>(&self, f: impl FnOnce(&T) -> O) -> O {
|
pub fn maybe_with_rt<O>(&self, f: impl FnOnce(&T) -> O) -> O {
|
||||||
if Runtime::current().is_none() {
|
if Runtime::current().is_err() {
|
||||||
f(&(self.initializer)())
|
f(&(self.initializer)())
|
||||||
} else {
|
} else {
|
||||||
self.with(f)
|
self.with(f)
|
||||||
|
|||||||
Reference in new issue
Block a user