move ScopeId methods to the runtime (#4723)
* move `ScopeId` methods to runtime methods * cargo check unused * wip... * wip... * fix consume context * fix test * guard hoisted * fix doc comment
This commit is contained in:
1 parent
c56bac04f4
commit
c29cef4ea3
40 files changed
+466
-697
No files matched your search
@@ -55,7 +55,7 @@ impl VirtualDom {
|
||||
self.scopes[scope.0].last_rendered_node = Some(LastRenderedNode::new(new_nodes));
|
||||
|
||||
if render_to.is_some() {
|
||||
self.runtime.get_state(scope).unwrap().mount(&self.runtime);
|
||||
self.runtime.get_state(scope).mount(&self.runtime);
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -84,7 +84,7 @@ impl VirtualDom {
|
||||
self.scopes[scope.0].last_rendered_node = Some(new_nodes);
|
||||
|
||||
if render_to.is_some() {
|
||||
self.runtime.get_state(scope).unwrap().mount(&self.runtime);
|
||||
self.runtime.get_state(scope).mount(&self.runtime);
|
||||
}
|
||||
|
||||
nodes
|
||||
@@ -146,7 +146,7 @@ impl VNode {
|
||||
// Now diff the scope
|
||||
dom.run_and_diff_scope(to, scope_id);
|
||||
|
||||
let height = dom.runtime.get_state(scope_id).unwrap().height;
|
||||
let height = dom.runtime.get_state(scope_id).height;
|
||||
dom.dirty_scopes.remove(&ScopeOrder::new(height, scope_id));
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ pub fn provide_create_error_boundary(create_error_boundary: fn() -> ErrorContext
|
||||
|
||||
/// Create an error boundary with the current error boundary factory (either hydration compatible or default)
|
||||
fn create_error_boundary() -> ErrorContext {
|
||||
let create_error_boundary: CreateErrorBoundary = try_consume_context().unwrap_or_default();
|
||||
let create_error_boundary = try_consume_context::<CreateErrorBoundary>().unwrap_or_default();
|
||||
(create_error_boundary.0)()
|
||||
}
|
||||
|
||||
|
||||
@@ -487,10 +487,8 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
|
||||
pub fn new<MaybeAsync: SpawnIfAsync<Marker, Ret>, Marker>(
|
||||
mut f: impl FnMut(Args) -> MaybeAsync + 'static,
|
||||
) -> Self {
|
||||
let runtime = Runtime::current().unwrap_or_else(|e| panic!("{}", e));
|
||||
let origin = runtime
|
||||
.current_scope_id()
|
||||
.unwrap_or_else(|e| panic!("{}", e));
|
||||
let runtime = Runtime::current();
|
||||
let origin = runtime.current_scope_id();
|
||||
let owner = crate::innerlude::current_owner::<generational_box::UnsyncStorage>();
|
||||
let callback = owner.insert_rc(Some(ExternalListenerCallback {
|
||||
callback: Box::new(move |event: Args| f(event).spawn()),
|
||||
@@ -502,10 +500,8 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
|
||||
/// Leak a new [`Callback`] that will not be dropped unless it is manually dropped.
|
||||
#[track_caller]
|
||||
pub fn leak(mut f: impl FnMut(Args) -> Ret + 'static) -> Self {
|
||||
let runtime = Runtime::current().unwrap_or_else(|e| panic!("{}", e));
|
||||
let origin = runtime
|
||||
.current_scope_id()
|
||||
.unwrap_or_else(|e| panic!("{}", e));
|
||||
let runtime = Runtime::current();
|
||||
let origin = runtime.current_scope_id();
|
||||
let callback = GenerationalBox::leak_rc(
|
||||
Some(ExternalListenerCallback {
|
||||
callback: Box::new(move |event: Args| f(event).spawn()),
|
||||
@@ -547,7 +543,7 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
|
||||
|
||||
/// Replace the function in the callback with a new one
|
||||
pub fn replace(&mut self, callback: Box<dyn FnMut(Args) -> Ret>) {
|
||||
let runtime = Runtime::current().unwrap_or_else(|e| panic!("{}", e));
|
||||
let runtime = Runtime::current();
|
||||
self.callback.set(Some(ExternalListenerCallback {
|
||||
callback,
|
||||
runtime: Rc::downgrade(&runtime),
|
||||
@@ -647,7 +643,7 @@ impl<T> ListenerCallback<T> {
|
||||
MaybeAsync: SpawnIfAsync<Marker>,
|
||||
{
|
||||
Self {
|
||||
origin: current_scope_id().expect("ListenerCallback must be created within a scope"),
|
||||
origin: current_scope_id(),
|
||||
callback: Rc::new(RefCell::new(move |event: Event<dyn Any>| {
|
||||
let data = event.data.downcast::<T>().unwrap();
|
||||
f(Event {
|
||||
@@ -665,8 +661,7 @@ impl<T> ListenerCallback<T> {
|
||||
/// This is expected to be called within a runtime scope. Make sure a runtime is current before
|
||||
/// calling this method.
|
||||
pub fn call(&self, event: Event<dyn Any>) {
|
||||
let runtime = Runtime::current().expect("ListenerCallback must be called within a runtime");
|
||||
runtime.with_scope_on_stack(self.origin, || {
|
||||
Runtime::current().with_scope_on_stack(self.origin, || {
|
||||
(self.callback.borrow_mut())(event);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
//!
|
||||
//! Each scope in dioxus has a single [Owner]
|
||||
|
||||
use generational_box::{AnyStorage, Owner, SyncStorage, UnsyncStorage};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
};
|
||||
|
||||
use generational_box::{AnyStorage, Owner, SyncStorage, UnsyncStorage};
|
||||
|
||||
use crate::{innerlude::current_scope_id, Runtime, ScopeId};
|
||||
|
||||
/// Run a closure with the given owner.
|
||||
///
|
||||
/// This will override the default owner for the current component.
|
||||
@@ -84,14 +81,5 @@ pub fn current_owner<S: AnyStorage>() -> Owner<S> {
|
||||
return owner;
|
||||
}
|
||||
|
||||
// Otherwise get the owner from the current scope
|
||||
current_scope_id().expect("in a virtual dom").owner()
|
||||
}
|
||||
|
||||
impl ScopeId {
|
||||
/// Get the owner for the current scope.
|
||||
#[track_caller]
|
||||
pub fn owner<S: AnyStorage>(self) -> Owner<S> {
|
||||
Runtime::with_scope(self, |cx| cx.owner::<S>()).unwrap_or_else(|e| panic!("{}", e))
|
||||
}
|
||||
crate::Runtime::current().current_owner()
|
||||
}
|
||||
@@ -1,23 +1,12 @@
|
||||
use crate::{innerlude::CapturedError, runtime::RuntimeError};
|
||||
use crate::{
|
||||
innerlude::SuspendedFuture, runtime::Runtime, Element, ScopeId, SuspenseContext, Task,
|
||||
};
|
||||
use crate::innerlude::CapturedError;
|
||||
use crate::{innerlude::SuspendedFuture, runtime::Runtime, Element, ScopeId, Task};
|
||||
use std::future::Future;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Get the current scope id
|
||||
pub fn current_scope_id() -> Result<ScopeId, RuntimeError> {
|
||||
Runtime::with(|rt| rt.current_scope_id().ok())
|
||||
.ok()
|
||||
.flatten()
|
||||
.ok_or(RuntimeError::new())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Check if the virtual dom is currently inside of the body of a component
|
||||
pub fn vdom_is_rendering() -> bool {
|
||||
Runtime::with(|rt| rt.rendering.get()).unwrap_or_default()
|
||||
pub fn current_scope_id() -> ScopeId {
|
||||
Runtime::with(|rt| rt.current_scope_id())
|
||||
}
|
||||
|
||||
/// Throw a [`CapturedError`] into the current scope. The error will bubble up to the nearest [`crate::ErrorBoundary()`] or the root of the app.
|
||||
@@ -30,7 +19,7 @@ pub fn vdom_is_rendering() -> bool {
|
||||
/// match reqwest::get("https://api.example.com").await {
|
||||
/// Ok(_) => unimplemented!(),
|
||||
/// // You can explicitly throw an error into a scope with throw_error
|
||||
/// Err(err) => ScopeId::APP.throw_error(err)
|
||||
/// Err(err) => dioxus::core::throw_error(err),
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
@@ -38,58 +27,40 @@ pub fn vdom_is_rendering() -> bool {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn throw_error(error: impl Into<CapturedError> + 'static) {
|
||||
current_scope_id()
|
||||
.unwrap_or_else(|e| panic!("{}", e))
|
||||
.throw_error(error)
|
||||
}
|
||||
|
||||
/// Get the suspense context the current scope is in
|
||||
pub fn suspense_context() -> Option<SuspenseContext> {
|
||||
current_scope_id()
|
||||
.unwrap_or_else(|e| panic!("{}", e))
|
||||
.suspense_context()
|
||||
Runtime::with(|rt| rt.throw_error(rt.current_scope_id(), error))
|
||||
}
|
||||
|
||||
/// Consume context from the current scope
|
||||
pub fn try_consume_context<T: 'static + Clone>() -> Option<T> {
|
||||
Runtime::with_current_scope(|cx| cx.consume_context::<T>())
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
/// Consume context from the current scope
|
||||
pub fn consume_context<T: 'static + Clone>() -> T {
|
||||
Runtime::with_current_scope(|cx| cx.consume_context::<T>())
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_else(|| panic!("Could not find context {}", std::any::type_name::<T>()))
|
||||
}
|
||||
|
||||
/// Consume context from the current scope
|
||||
pub fn consume_context_from_scope<T: 'static + Clone>(scope_id: ScopeId) -> Option<T> {
|
||||
Runtime::with(|rt| {
|
||||
rt.get_state(scope_id)
|
||||
.and_then(|cx| cx.consume_context::<T>())
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
Runtime::current()
|
||||
.try_get_state(scope_id)
|
||||
.and_then(|cx| cx.consume_context::<T>())
|
||||
}
|
||||
|
||||
/// Check if the current scope has a context
|
||||
pub fn has_context<T: 'static + Clone>() -> Option<T> {
|
||||
Runtime::with_current_scope(|cx| cx.has_context::<T>())
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
/// Provide context to the current scope
|
||||
pub fn provide_context<T: 'static + Clone>(value: T) -> T {
|
||||
Runtime::with_current_scope(|cx| cx.provide_context(value)).unwrap()
|
||||
Runtime::with_current_scope(|cx| cx.provide_context(value))
|
||||
}
|
||||
|
||||
/// Provide a context to the root scope
|
||||
pub fn provide_root_context<T: 'static + Clone>(value: T) -> T {
|
||||
Runtime::with_current_scope(|cx| cx.provide_root_context(value)).unwrap()
|
||||
Runtime::with_current_scope(|cx| cx.provide_root_context(value))
|
||||
}
|
||||
|
||||
/// Suspended the current component on a specific task and then return None
|
||||
@@ -128,7 +99,7 @@ pub fn suspend(task: Task) -> Element {
|
||||
///
|
||||
#[doc = include_str!("../docs/common_spawn_errors.md")]
|
||||
pub fn spawn_isomorphic(fut: impl Future<Output = ()> + 'static) -> Task {
|
||||
Runtime::with_current_scope(|cx| cx.spawn_isomorphic(fut)).unwrap()
|
||||
Runtime::with_current_scope(|cx| cx.spawn_isomorphic(fut))
|
||||
}
|
||||
|
||||
/// Spawns the future and returns the [`Task`]. This task will automatically be canceled when the component is dropped.
|
||||
@@ -154,12 +125,12 @@ pub fn spawn_isomorphic(fut: impl Future<Output = ()> + 'static) -> Task {
|
||||
///
|
||||
#[doc = include_str!("../docs/common_spawn_errors.md")]
|
||||
pub fn spawn(fut: impl Future<Output = ()> + 'static) -> Task {
|
||||
Runtime::with_current_scope(|cx| cx.spawn(fut)).unwrap()
|
||||
Runtime::with_current_scope(|cx| cx.spawn(fut))
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
Runtime::with_current_scope(|cx| cx.queue_effect(f)).unwrap()
|
||||
Runtime::with_current_scope(|cx| cx.queue_effect(f))
|
||||
}
|
||||
|
||||
/// Spawn a future that Dioxus won't clean up when this component is unmounted
|
||||
@@ -215,8 +186,8 @@ pub fn queue_effect(f: impl FnOnce() + 'static) {
|
||||
/// ```
|
||||
///
|
||||
#[doc = include_str!("../docs/common_spawn_errors.md")]
|
||||
pub fn spawn_forever(fut: impl Future<Output = ()> + 'static) -> Option<Task> {
|
||||
Runtime::with_scope(ScopeId::ROOT, |cx| cx.spawn(fut)).ok()
|
||||
pub fn spawn_forever(fut: impl Future<Output = ()> + 'static) -> Task {
|
||||
Runtime::with_scope(ScopeId::ROOT, |cx| cx.spawn(fut))
|
||||
}
|
||||
|
||||
/// Informs the scheduler that this task is no longer needed and should be removed.
|
||||
@@ -311,31 +282,29 @@ pub fn remove_future(id: Task) {
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn use_hook<State: Clone + 'static>(initializer: impl FnOnce() -> State) -> State {
|
||||
Runtime::with_current_scope(|cx| cx.use_hook(initializer)).unwrap()
|
||||
Runtime::with_current_scope(|cx| cx.use_hook(initializer))
|
||||
}
|
||||
|
||||
/// 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() -> usize {
|
||||
Runtime::with_current_scope(|cx| cx.generation()).unwrap()
|
||||
Runtime::with_current_scope(|cx| cx.generation())
|
||||
}
|
||||
|
||||
/// Get the parent of the current scope if it exists.
|
||||
pub fn parent_scope() -> Option<ScopeId> {
|
||||
Runtime::with_current_scope(|cx| cx.parent_id())
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
/// Mark the current scope as dirty, causing it to re-render.
|
||||
pub fn needs_update() {
|
||||
let _ = Runtime::with_current_scope(|cx| cx.needs_update());
|
||||
Runtime::with_current_scope(|cx| cx.needs_update());
|
||||
}
|
||||
|
||||
/// Mark the current scope as dirty, causing it to re-render.
|
||||
pub fn needs_update_any(id: ScopeId) {
|
||||
let _ = Runtime::with_current_scope(|cx| cx.needs_update_any(id));
|
||||
Runtime::with_current_scope(|cx| cx.needs_update_any(id));
|
||||
}
|
||||
|
||||
/// Schedule an update for the current component.
|
||||
@@ -349,7 +318,7 @@ pub fn needs_update_any(id: ScopeId) {
|
||||
/// You should prefer [`schedule_update_any`] if you need to update multiple components.
|
||||
#[track_caller]
|
||||
pub fn schedule_update() -> Arc<dyn Fn() + Send + Sync> {
|
||||
Runtime::with_current_scope(|cx| cx.schedule_update()).unwrap_or_else(|e| panic!("{}", e))
|
||||
Runtime::with_current_scope(|cx| cx.schedule_update())
|
||||
}
|
||||
|
||||
/// Schedule an update for any component given its [`ScopeId`].
|
||||
@@ -362,7 +331,7 @@ pub fn schedule_update() -> Arc<dyn Fn() + Send + Sync> {
|
||||
/// If the desired behavior is to schedule invalidation of the current rendering of a component, use [`ReactiveContext`](crate::reactive_context::ReactiveContext) instead.
|
||||
#[track_caller]
|
||||
pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
|
||||
Runtime::with_current_scope(|cx| cx.schedule_update_any()).unwrap_or_else(|e| panic!("{}", e))
|
||||
Runtime::with_current_scope(|cx| cx.schedule_update_any())
|
||||
}
|
||||
|
||||
/// Creates a callback that will be run before the component is removed.
|
||||
@@ -468,12 +437,12 @@ pub fn use_after_render(f: impl FnMut() + 'static) {
|
||||
/// 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
|
||||
pub fn before_render(f: impl FnMut() + 'static) {
|
||||
let _ = Runtime::with_current_scope(|cx| cx.push_before_render(f));
|
||||
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
|
||||
pub fn after_render(f: impl FnMut() + 'static) {
|
||||
let _ = Runtime::with_current_scope(|cx| cx.push_after_render(f));
|
||||
Runtime::with_current_scope(|cx| cx.push_after_render(f));
|
||||
}
|
||||
|
||||
/// Use a hook with a cleanup function
|
||||
@@ -486,18 +455,3 @@ pub fn use_hook_with_cleanup<T: Clone + 'static>(
|
||||
use_drop(move || cleanup(_value));
|
||||
value
|
||||
}
|
||||
|
||||
/// Force every component to be dirty and require a re-render. Used by hot-reloading.
|
||||
///
|
||||
/// This might need to change to a different flag in the event hooks order changes within components.
|
||||
/// What we really need is a way to mark components as needing a complete rebuild if they were hit by changes.
|
||||
pub fn force_all_dirty() {
|
||||
Runtime::with(|rt| {
|
||||
rt.scope_states.borrow_mut().iter().for_each(|state| {
|
||||
if let Some(scope) = state.as_ref() {
|
||||
scope.needs_update();
|
||||
}
|
||||
});
|
||||
})
|
||||
.expect("Runtime to exist");
|
||||
}
|
||||
+13
-14
@@ -91,20 +91,19 @@ pub(crate) mod innerlude {
|
||||
|
||||
pub use crate::innerlude::{
|
||||
anyhow, consume_context, consume_context_from_scope, current_owner, current_scope_id,
|
||||
fc_to_builder, force_all_dirty, generation, has_context, needs_update, needs_update_any,
|
||||
parent_scope, provide_context, provide_create_error_boundary, provide_root_context,
|
||||
queue_effect, remove_future, schedule_update, schedule_update_any, spawn, spawn_forever,
|
||||
spawn_isomorphic, suspend, suspense_context, throw_error, try_consume_context,
|
||||
use_after_render, use_before_render, use_drop, use_hook, use_hook_with_cleanup,
|
||||
vdom_is_rendering, with_owner, AnyValue, AnyhowContext, Attribute, AttributeValue, Callback,
|
||||
CapturedError, Component, ComponentFunction, DynamicNode, Element, ElementId, Error,
|
||||
ErrorBoundary, ErrorContext, Event, EventHandler, Fragment, HasAttributes, IntoAttributeValue,
|
||||
IntoDynNode, LaunchConfig, ListenerCallback, MarkerWrapper, Mutation, Mutations, NoOpMutations,
|
||||
OptionStringFromMarker, Properties, ReactiveContext, RenderError, Result, Runtime,
|
||||
RuntimeGuard, ScopeId, ScopeState, SpawnIfAsync, SubscriberList, Subscribers, SuperFrom,
|
||||
SuperInto, SuspendedFuture, SuspenseBoundary, SuspenseBoundaryProps, SuspenseContext, Task,
|
||||
Template, TemplateAttribute, TemplateNode, VComponent, VNode, VNodeInner, VPlaceholder, VText,
|
||||
VirtualDom, WriteMutations,
|
||||
fc_to_builder, generation, has_context, needs_update, needs_update_any, parent_scope,
|
||||
provide_context, provide_create_error_boundary, provide_root_context, queue_effect,
|
||||
remove_future, schedule_update, schedule_update_any, spawn, spawn_forever, spawn_isomorphic,
|
||||
suspend, throw_error, try_consume_context, use_after_render, use_before_render, use_drop,
|
||||
use_hook, use_hook_with_cleanup, with_owner, AnyValue, AnyhowContext, Attribute,
|
||||
AttributeValue, Callback, CapturedError, Component, ComponentFunction, DynamicNode, Element,
|
||||
ElementId, Error, ErrorBoundary, ErrorContext, Event, EventHandler, Fragment, HasAttributes,
|
||||
IntoAttributeValue, IntoDynNode, LaunchConfig, ListenerCallback, MarkerWrapper, Mutation,
|
||||
Mutations, NoOpMutations, OptionStringFromMarker, Properties, ReactiveContext, RenderError,
|
||||
Result, Runtime, RuntimeGuard, ScopeId, ScopeState, SpawnIfAsync, SubscriberList, Subscribers,
|
||||
SuperFrom, SuperInto, SuspendedFuture, SuspenseBoundary, SuspenseBoundaryProps,
|
||||
SuspenseContext, Task, Template, TemplateAttribute, TemplateNode, VComponent, VNode,
|
||||
VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations,
|
||||
};
|
||||
|
||||
pub use anyhow::Ok;
|
||||
|
||||
@@ -62,11 +62,7 @@ impl ReactiveContext {
|
||||
}
|
||||
let _ = tx.unbounded_send(());
|
||||
};
|
||||
let _self = Self::new_with_callback(
|
||||
callback,
|
||||
current_scope_id().unwrap_or_else(|e| panic!("{}", e)),
|
||||
origin,
|
||||
);
|
||||
let _self = Self::new_with_callback(callback, current_scope_id(), origin);
|
||||
(_self, rx)
|
||||
}
|
||||
|
||||
@@ -86,7 +82,7 @@ impl ReactiveContext {
|
||||
scope: None,
|
||||
};
|
||||
|
||||
let owner = scope.owner();
|
||||
let owner = Runtime::current().scope_owner(scope);
|
||||
|
||||
let self_ = Self {
|
||||
scope,
|
||||
|
||||
+196
-99
@@ -1,8 +1,11 @@
|
||||
use crate::arena::ElementRef;
|
||||
use crate::innerlude::{DirtyTasks, Effect};
|
||||
use crate::nodes::VNodeMount;
|
||||
use crate::scheduler::ScopeOrder;
|
||||
use crate::scope_context::SuspenseLocation;
|
||||
use crate::{arena::ElementRef, CapturedError};
|
||||
use crate::{
|
||||
innerlude::{DirtyTasks, Effect},
|
||||
SuspenseContext,
|
||||
};
|
||||
use crate::{
|
||||
innerlude::{LocalTask, SchedulerMsg},
|
||||
scope_context::Scope,
|
||||
@@ -10,11 +13,11 @@ use crate::{
|
||||
Task,
|
||||
};
|
||||
use crate::{AttributeValue, ElementId, Event};
|
||||
use generational_box::{AnyStorage, Owner};
|
||||
use slab::Slab;
|
||||
use slotmap::DefaultKey;
|
||||
use std::any::Any;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt;
|
||||
use std::{
|
||||
cell::{Cell, Ref, RefCell},
|
||||
rc::Rc,
|
||||
@@ -27,8 +30,6 @@ thread_local! {
|
||||
|
||||
/// A global runtime that is shared across all scopes that provides the async runtime and context API
|
||||
pub struct Runtime {
|
||||
pub(crate) scope_states: RefCell<Vec<Option<Scope>>>,
|
||||
|
||||
// We use this to track the current scope
|
||||
// This stack should only be modified through [`Runtime::with_scope_on_stack`] to ensure that the stack is correctly restored
|
||||
scope_stack: RefCell<Vec<ScopeId>>,
|
||||
@@ -37,6 +38,9 @@ pub struct Runtime {
|
||||
// This stack should only be modified through [`Runtime::with_suspense_location`] to ensure that the stack is correctly restored
|
||||
suspense_stack: RefCell<Vec<SuspenseLocation>>,
|
||||
|
||||
// A hand-rolled slab of scope states
|
||||
pub(crate) scope_states: RefCell<Vec<Option<Scope>>>,
|
||||
|
||||
// We use this to track the current task
|
||||
pub(crate) current_task: Cell<Option<Task>>,
|
||||
|
||||
@@ -89,18 +93,55 @@ impl Runtime {
|
||||
}
|
||||
|
||||
/// Get the current runtime
|
||||
pub fn current() -> Result<Rc<Self>, RuntimeError> {
|
||||
pub fn current() -> Rc<Self> {
|
||||
RUNTIMES
|
||||
.with(|stack| stack.borrow().last().cloned())
|
||||
.ok_or(RuntimeError::new())
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"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}}\");
|
||||
}});
|
||||
}})
|
||||
}}
|
||||
```"
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to get the current runtime, returning None if it doesn't exist (outside the context of a dioxus app)
|
||||
pub fn try_current() -> Option<Rc<Self>> {
|
||||
RUNTIMES.with(|stack| stack.borrow().last().cloned())
|
||||
}
|
||||
|
||||
/// Wrap a closure so that it always runs in the runtime that is currently active
|
||||
pub fn wrap_closure<'a, I, O>(f: impl Fn(I) -> O + 'a) -> impl Fn(I) -> O + 'a {
|
||||
let current_runtime = Self::current().unwrap();
|
||||
let current_scope = current_runtime.current_scope_id().ok();
|
||||
move |input| match current_scope {
|
||||
Some(scope) => current_runtime.on_scope(scope, || f(input)),
|
||||
let current_runtime = Self::current();
|
||||
move |input| match current_runtime.try_current_scope_id() {
|
||||
Some(scope) => current_runtime.in_scope(scope, || f(input)),
|
||||
None => {
|
||||
let _runtime_guard = RuntimeGuard::new(current_runtime.clone());
|
||||
f(input)
|
||||
@@ -140,7 +181,7 @@ impl Runtime {
|
||||
let borrow = self.scope_states.borrow();
|
||||
if let Some(scope) = &borrow[id.0] {
|
||||
// Manually drop tasks, hooks, and contexts inside of the runtime
|
||||
self.on_scope(id, || {
|
||||
self.in_scope(id, || {
|
||||
// Drop all spawned tasks - order doesn't matter since tasks don't rely on eachother
|
||||
// In theory nested tasks might not like this
|
||||
for id in scope.spawned_tasks.take() {
|
||||
@@ -165,19 +206,31 @@ impl Runtime {
|
||||
self.scope_states.borrow_mut()[id.0].take();
|
||||
}
|
||||
|
||||
/// Get the owner for the current scope.
|
||||
#[track_caller]
|
||||
pub fn current_owner<S: AnyStorage>(&self) -> Owner<S> {
|
||||
self.get_state(self.current_scope_id()).owner()
|
||||
}
|
||||
|
||||
/// Get the owner for the current scope.
|
||||
#[track_caller]
|
||||
pub fn scope_owner<S: AnyStorage>(&self, scope: ScopeId) -> Owner<S> {
|
||||
self.get_state(scope).owner()
|
||||
}
|
||||
|
||||
/// Get the current scope id
|
||||
pub(crate) fn current_scope_id(&self) -> Result<ScopeId, RuntimeError> {
|
||||
self.scope_stack
|
||||
.borrow()
|
||||
.last()
|
||||
.copied()
|
||||
.ok_or(RuntimeError { _priv: () })
|
||||
pub fn current_scope_id(&self) -> ScopeId {
|
||||
self.scope_stack.borrow().last().copied().unwrap()
|
||||
}
|
||||
|
||||
/// Try to get the current scope id, returning None if it we aren't actively inside a scope
|
||||
pub fn try_current_scope_id(&self) -> Option<ScopeId> {
|
||||
self.scope_stack.borrow().last().copied()
|
||||
}
|
||||
|
||||
/// Call this function with the current scope set to the given scope
|
||||
///
|
||||
/// Useful in a limited number of scenarios
|
||||
pub fn on_scope<O>(self: &Rc<Self>, id: ScopeId, f: impl FnOnce() -> O) -> O {
|
||||
#[track_caller]
|
||||
pub fn in_scope<O>(self: &Rc<Self>, id: ScopeId, f: impl FnOnce() -> O) -> O {
|
||||
let _runtime_guard = RuntimeGuard::new(self.clone());
|
||||
{
|
||||
self.push_scope(id);
|
||||
@@ -236,7 +289,18 @@ impl Runtime {
|
||||
/// 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(crate) fn get_state(&self, id: ScopeId) -> Option<Ref<'_, Scope>> {
|
||||
pub(crate) fn get_state(&self, id: ScopeId) -> Ref<'_, Scope> {
|
||||
Ref::filter_map(self.scope_states.borrow(), |scopes| {
|
||||
scopes.get(id.0).and_then(|f| f.as_ref())
|
||||
})
|
||||
.ok()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// 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(crate) fn try_get_state(&self, id: ScopeId) -> Option<Ref<'_, Scope>> {
|
||||
Ref::filter_map(self.scope_states.borrow(), |contexts| {
|
||||
contexts.get(id.0).and_then(|f| f.as_ref())
|
||||
})
|
||||
@@ -254,30 +318,19 @@ impl Runtime {
|
||||
}
|
||||
|
||||
/// Runs a function with the current runtime
|
||||
pub(crate) fn with<R>(f: impl FnOnce(&Runtime) -> R) -> Result<R, RuntimeError> {
|
||||
Self::current().map(|r| f(&r))
|
||||
pub(crate) fn with<R>(callback: impl FnOnce(&Runtime) -> R) -> R {
|
||||
callback(&Self::current())
|
||||
}
|
||||
|
||||
/// Runs a function with the current scope
|
||||
pub(crate) fn with_current_scope<R>(f: impl FnOnce(&Scope) -> R) -> Result<R, RuntimeError> {
|
||||
Self::with(|rt| {
|
||||
rt.current_scope_id()
|
||||
.ok()
|
||||
.and_then(|scope| rt.get_state(scope).map(|sc| f(&sc)))
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
.ok_or(RuntimeError::new())
|
||||
pub(crate) fn with_current_scope<R>(callback: impl FnOnce(&Scope) -> R) -> R {
|
||||
Self::with(|rt| Self::with_scope(rt.current_scope_id(), callback))
|
||||
}
|
||||
|
||||
/// Runs a function with the current scope
|
||||
pub(crate) fn with_scope<R>(
|
||||
scope: ScopeId,
|
||||
callback: impl FnOnce(&Scope) -> R,
|
||||
) -> Result<R, RuntimeError> {
|
||||
Self::with(|rt| rt.get_state(scope).map(|scopestate| callback(&scopestate)))
|
||||
.expect("Runtime should exist")
|
||||
.ok_or(RuntimeError::new())
|
||||
pub(crate) fn with_scope<R>(scope: ScopeId, callback: impl FnOnce(&Scope) -> R) -> R {
|
||||
let rt = Runtime::current();
|
||||
Self::in_scope(&rt, scope, || callback(&rt.get_state(scope)))
|
||||
}
|
||||
|
||||
/// Finish a render. This will mark all effects as ready to run and send the render signal.
|
||||
@@ -296,6 +349,7 @@ impl Runtime {
|
||||
if self.suspended_tasks.get() == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If this is not a suspended scope, and we are under a frozen context, then we should
|
||||
let scopes = self.scope_states.borrow();
|
||||
let scope = &scopes[scope_id.0].as_ref().unwrap();
|
||||
@@ -441,6 +495,107 @@ impl Runtime {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume context from the current scope
|
||||
pub fn consume_context<T: 'static + Clone>(&self, id: ScopeId) -> Option<T> {
|
||||
self.get_state(id).consume_context::<T>()
|
||||
}
|
||||
|
||||
/// Consume context from the current scope
|
||||
pub fn consume_context_from_scope<T: 'static + Clone>(&self, scope_id: ScopeId) -> Option<T> {
|
||||
self.get_state(scope_id).consume_context::<T>()
|
||||
}
|
||||
|
||||
/// Check if the current scope has a context
|
||||
pub fn has_context<T: 'static + Clone>(&self, id: ScopeId) -> Option<T> {
|
||||
self.get_state(id).has_context::<T>()
|
||||
}
|
||||
|
||||
/// Provide context to the current scope
|
||||
pub fn provide_context<T: 'static + Clone>(&self, id: ScopeId, value: T) -> T {
|
||||
self.get_state(id).provide_context(value)
|
||||
}
|
||||
|
||||
/// Get the parent of the current scope if it exists
|
||||
pub fn parent_scope(&self, scope: ScopeId) -> Option<ScopeId> {
|
||||
self.get_state(scope).parent_id()
|
||||
}
|
||||
|
||||
/// Check if the current scope is a descendant of the given scope
|
||||
pub fn is_descendant_of(&self, us: ScopeId, other: ScopeId) -> bool {
|
||||
let mut current = us;
|
||||
while let Some(parent) = self.parent_scope(current) {
|
||||
if parent == other {
|
||||
return true;
|
||||
}
|
||||
current = parent;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Mark the current scope as dirty, causing it to re-render
|
||||
pub fn needs_update(&self, scope: ScopeId) {
|
||||
self.get_state(scope).needs_update();
|
||||
}
|
||||
|
||||
/// Get the height of the current scope
|
||||
pub fn height(&self, id: ScopeId) -> u32 {
|
||||
self.get_state(id).height
|
||||
}
|
||||
|
||||
/// Throw a [`CapturedError`] into a scope. The error will bubble up to the nearest [`ErrorBoundary`](crate::ErrorBoundary) or the root of the app.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// fn Component() -> Element {
|
||||
/// let request = spawn(async move {
|
||||
/// match reqwest::get("https://api.example.com").await {
|
||||
/// Ok(_) => unimplemented!(),
|
||||
/// // You can explicitly throw an error into a scope with throw_error
|
||||
/// Err(err) => dioxus::core::Runtime::current().throw_error(ScopeId::APP, err),
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
/// ```
|
||||
pub fn throw_error(&self, id: ScopeId, error: impl Into<CapturedError> + 'static) {
|
||||
let error = error.into();
|
||||
if let Some(cx) = self.consume_context::<crate::ErrorContext>(id) {
|
||||
cx.insert_error(error)
|
||||
} else {
|
||||
tracing::error!(
|
||||
"Tried to throw an error into an error boundary, but failed to locate a boundary: {:?}",
|
||||
error
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the suspense context the current scope is in
|
||||
pub fn suspense_context(&self) -> Option<SuspenseContext> {
|
||||
self.get_state(self.current_scope_id())
|
||||
.suspense_location()
|
||||
.suspense_context()
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// Force every component to be dirty and require a re-render. Used by hot-reloading.
|
||||
///
|
||||
/// This might need to change to a different flag in the event hooks order changes within components.
|
||||
/// What we really need is a way to mark components as needing a complete rebuild if they were hit by changes.
|
||||
pub fn force_all_dirty(&self) {
|
||||
self.scope_states.borrow_mut().iter().for_each(|state| {
|
||||
if let Some(scope) = state.as_ref() {
|
||||
scope.needs_update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Check if the virtual dom is currently rendering
|
||||
pub fn vdom_is_rendering(&self) -> bool {
|
||||
self.rendering.get()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@@ -454,7 +609,7 @@ impl Runtime {
|
||||
/// }
|
||||
///
|
||||
/// fn app() -> Element {
|
||||
/// rsx! { Component { runtime: Runtime::current().unwrap() } }
|
||||
/// rsx! { Component { runtime: Runtime::current() } }
|
||||
/// }
|
||||
///
|
||||
/// // In a dynamic library
|
||||
@@ -492,61 +647,3 @@ impl Drop for RuntimeGuard {
|
||||
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 {}
|
||||
@@ -13,8 +13,8 @@ impl VirtualDom {
|
||||
props: BoxedAnyProps,
|
||||
name: &'static str,
|
||||
) -> &mut ScopeState {
|
||||
let parent_id = self.runtime.current_scope_id().ok();
|
||||
let height = match parent_id.and_then(|id| self.runtime.get_state(id)) {
|
||||
let parent_id = self.runtime.try_current_scope_id();
|
||||
let height = match parent_id.and_then(|id| self.runtime.try_get_state(id)) {
|
||||
Some(parent) => parent.height() + 1,
|
||||
None => 0,
|
||||
};
|
||||
@@ -46,7 +46,7 @@ impl VirtualDom {
|
||||
#[track_caller]
|
||||
pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> Element {
|
||||
// Ensure we are currently inside a `Runtime`.
|
||||
crate::Runtime::current().unwrap_or_else(|e| panic!("{}", e));
|
||||
crate::Runtime::current();
|
||||
|
||||
self.runtime.clone().with_scope_on_stack(scope_id, || {
|
||||
let scope = &self.scopes[scope_id.0];
|
||||
@@ -84,7 +84,7 @@ impl VirtualDom {
|
||||
}
|
||||
};
|
||||
|
||||
self.handle_element_return(&mut render_return, scope_id, &scope.state());
|
||||
self.handle_element_return(&mut render_return, &scope.state());
|
||||
render_return
|
||||
})
|
||||
})
|
||||
@@ -105,23 +105,16 @@ impl VirtualDom {
|
||||
}
|
||||
|
||||
/// Insert any errors, or suspended tasks from an element return into the runtime
|
||||
fn handle_element_return(&self, node: &mut Element, scope_id: ScopeId, scope_state: &Scope) {
|
||||
fn handle_element_return(&self, node: &mut Element, scope: &Scope) {
|
||||
match node {
|
||||
Err(RenderError::Error(e)) => {
|
||||
tracing::error!(
|
||||
"Error while rendering component `{}`:\n{e}",
|
||||
scope_state.name
|
||||
);
|
||||
tracing::error!("Error while rendering component `{}`:\n{e}", scope.name);
|
||||
throw_error(e.clone());
|
||||
}
|
||||
Err(RenderError::Suspended(e)) => {
|
||||
let task = e.task();
|
||||
// Insert the task into the nearest suspense boundary if it exists
|
||||
let boundary = self
|
||||
.runtime
|
||||
.get_state(scope_id)
|
||||
.unwrap()
|
||||
.suspense_location();
|
||||
let boundary = scope.suspense_location();
|
||||
let already_suspended = self
|
||||
.runtime
|
||||
.tasks
|
||||
@@ -130,7 +123,7 @@ impl VirtualDom {
|
||||
.expect("Suspended on a task that no longer exists")
|
||||
.suspend(boundary.clone());
|
||||
if !already_suspended {
|
||||
tracing::trace!("Suspending {:?} on {:?}", scope_id, task);
|
||||
tracing::trace!("Suspending {:?} on {:?}", scope.id, task);
|
||||
// Add this task to the suspended tasks list of the boundary
|
||||
if let SuspenseLocation::UnderSuspense(boundary) = &boundary {
|
||||
boundary.add_suspended_task(e.clone());
|
||||
@@ -142,9 +135,7 @@ impl VirtualDom {
|
||||
}
|
||||
Ok(_) => {
|
||||
// If the render was successful, we can move the render generation forward by one
|
||||
scope_state
|
||||
.render_count
|
||||
.set(scope_state.render_count.get() + 1);
|
||||
scope.render_count.set(scope.render_count.get() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{
|
||||
innerlude::{CapturedError, SchedulerMsg, SuspenseContext},
|
||||
runtime::RuntimeError,
|
||||
innerlude::{SchedulerMsg, SuspenseContext},
|
||||
Runtime, ScopeId, Task,
|
||||
};
|
||||
use generational_box::{AnyStorage, Owner};
|
||||
@@ -96,7 +95,7 @@ impl Scope {
|
||||
}
|
||||
|
||||
fn sender(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
|
||||
Runtime::with(|rt| rt.sender.clone()).unwrap_or_else(|e| panic!("{}", e))
|
||||
Runtime::current().sender.clone()
|
||||
}
|
||||
|
||||
/// Mount the scope and queue any pending effects if it is not already mounted
|
||||
@@ -133,12 +132,12 @@ impl Scope {
|
||||
}
|
||||
|
||||
/// Mark this scope as dirty, and schedule a render for it.
|
||||
pub fn needs_update(&self) {
|
||||
pub(crate) fn needs_update(&self) {
|
||||
self.needs_update_any(self.id)
|
||||
}
|
||||
|
||||
/// Mark this scope as dirty, and schedule a render for it.
|
||||
pub fn needs_update_any(&self, id: ScopeId) {
|
||||
pub(crate) fn needs_update_any(&self, id: ScopeId) {
|
||||
self.sender()
|
||||
.unbounded_send(SchedulerMsg::Immediate(id))
|
||||
.expect("Scheduler to exist if scope exists");
|
||||
@@ -151,7 +150,7 @@ impl Scope {
|
||||
/// Note: The function returned by this method will schedule an update for the current component even if it has already updated between when `schedule_update` was called and when the returned function is called.
|
||||
/// If the desired behavior is to invalidate the current rendering of the current component (and no-op if already invalidated)
|
||||
/// [`subscribe`](crate::reactive_context::ReactiveContext::subscribe) to the [`current`](crate::reactive_context::ReactiveContext::current) [`ReactiveContext`](crate::reactive_context::ReactiveContext) instead.
|
||||
pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
|
||||
pub(crate) fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
|
||||
let (chan, id) = (self.sender(), self.id);
|
||||
Arc::new(move || drop(chan.unbounded_send(SchedulerMsg::Immediate(id))))
|
||||
}
|
||||
@@ -164,7 +163,7 @@ impl Scope {
|
||||
///
|
||||
/// Note: It does not matter when `schedule_update_any` is called: the returned function will invalidate what ever generation of the specified component is current when returned function is called.
|
||||
/// If the desired behavior is to schedule invalidation of the current rendering of a component, use [`ReactiveContext`](crate::reactive_context::ReactiveContext) instead.
|
||||
pub fn schedule_update_any(&self) -> Arc<dyn Fn(ScopeId) + Send + Sync> {
|
||||
pub(crate) fn schedule_update_any(&self) -> Arc<dyn Fn(ScopeId) + Send + Sync> {
|
||||
let chan = self.sender();
|
||||
Arc::new(move |id| {
|
||||
_ = chan.unbounded_send(SchedulerMsg::Immediate(id));
|
||||
@@ -172,7 +171,7 @@ impl Scope {
|
||||
}
|
||||
|
||||
/// Get the owner for the current scope.
|
||||
pub fn owner<S: AnyStorage>(&self) -> Owner<S> {
|
||||
pub(crate) fn owner<S: AnyStorage>(&self) -> Owner<S> {
|
||||
match self.has_context() {
|
||||
Some(rt) => rt,
|
||||
None => {
|
||||
@@ -183,7 +182,7 @@ impl Scope {
|
||||
}
|
||||
|
||||
/// Return any context of type T if it exists on this scope
|
||||
pub fn has_context<T: 'static + Clone>(&self) -> Option<T> {
|
||||
pub(crate) fn has_context<T: 'static + Clone>(&self) -> Option<T> {
|
||||
self.shared_contexts
|
||||
.borrow()
|
||||
.iter()
|
||||
@@ -194,49 +193,23 @@ impl Scope {
|
||||
/// Try to retrieve a shared state with type `T` from any parent scope.
|
||||
///
|
||||
/// Clones the state if it exists.
|
||||
pub fn consume_context<T: 'static + Clone>(&self) -> Option<T> {
|
||||
tracing::trace!(
|
||||
"looking for context {} ({:?}) in {:?}",
|
||||
std::any::type_name::<T>(),
|
||||
std::any::TypeId::of::<T>(),
|
||||
self.id
|
||||
);
|
||||
if let Some(this_ctx) = self.has_context() {
|
||||
pub(crate) fn consume_context<T: 'static + Clone>(&self) -> Option<T> {
|
||||
if let Some(this_ctx) = self.has_context::<T>() {
|
||||
return Some(this_ctx);
|
||||
}
|
||||
|
||||
let mut search_parent = self.parent_id;
|
||||
let cur_runtime = Runtime::with(|runtime| {
|
||||
|
||||
Runtime::with(|runtime| {
|
||||
while let Some(parent_id) = search_parent {
|
||||
let Some(parent) = runtime.get_state(parent_id) else {
|
||||
tracing::error!("Parent scope {:?} not found", parent_id);
|
||||
return None;
|
||||
};
|
||||
tracing::trace!(
|
||||
"looking for context {} ({:?}) in {:?}",
|
||||
std::any::type_name::<T>(),
|
||||
std::any::TypeId::of::<T>(),
|
||||
parent.id
|
||||
);
|
||||
if let Some(shared) = parent.has_context() {
|
||||
let parent = runtime.try_get_state(parent_id)?;
|
||||
if let Some(shared) = parent.has_context::<T>() {
|
||||
return Some(shared);
|
||||
}
|
||||
search_parent = parent.parent_id;
|
||||
}
|
||||
None
|
||||
});
|
||||
|
||||
match cur_runtime.ok().flatten() {
|
||||
Some(ctx) => Some(ctx),
|
||||
None => {
|
||||
tracing::trace!(
|
||||
"context {} ({:?}) not found",
|
||||
std::any::type_name::<T>(),
|
||||
std::any::TypeId::of::<T>()
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Inject a `Box<dyn Any>` into the context of this scope
|
||||
@@ -281,13 +254,7 @@ impl Scope {
|
||||
/// rsx!(div { "hello {state.0}" })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn provide_context<T: 'static + Clone>(&self, value: T) -> T {
|
||||
tracing::trace!(
|
||||
"providing context {} ({:?}) in {:?}",
|
||||
std::any::type_name::<T>(),
|
||||
std::any::TypeId::of::<T>(),
|
||||
self.id
|
||||
);
|
||||
pub(crate) fn provide_context<T: 'static + Clone>(&self, value: T) -> T {
|
||||
let mut contexts = self.shared_contexts.borrow_mut();
|
||||
|
||||
// If the context exists, swap it out for the new value
|
||||
@@ -312,14 +279,8 @@ impl Scope {
|
||||
///
|
||||
/// Note that you should be checking if the context existed before trying to provide a new one. Providing a context
|
||||
/// when a context already exists will swap the context out for the new one, which may not be what you want.
|
||||
pub fn provide_root_context<T: 'static + Clone>(&self, context: T) -> T {
|
||||
Runtime::with(|runtime| {
|
||||
runtime
|
||||
.get_state(ScopeId::ROOT)
|
||||
.unwrap()
|
||||
.provide_context(context)
|
||||
})
|
||||
.expect("Runtime to exist")
|
||||
pub(crate) fn provide_root_context<T: 'static + Clone>(&self, context: T) -> T {
|
||||
Runtime::with(|runtime| runtime.get_state(ScopeId::ROOT).provide_context(context))
|
||||
}
|
||||
|
||||
/// Start a new future on the same thread as the rest of the VirtualDom.
|
||||
@@ -348,108 +309,29 @@ impl Scope {
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
pub fn spawn_isomorphic(&self, fut: impl Future<Output = ()> + 'static) -> Task {
|
||||
let id = Runtime::with(|rt| rt.spawn_isomorphic(self.id, fut)).expect("Runtime to exist");
|
||||
pub(crate) fn spawn_isomorphic(&self, fut: impl Future<Output = ()> + 'static) -> Task {
|
||||
let id = Runtime::with(|rt| rt.spawn_isomorphic(self.id, fut));
|
||||
self.spawned_tasks.borrow_mut().insert(id);
|
||||
id
|
||||
}
|
||||
|
||||
/// Spawns the future and returns the [`Task`]
|
||||
pub fn spawn(&self, fut: impl Future<Output = ()> + 'static) -> Task {
|
||||
let id = Runtime::with(|rt| rt.spawn(self.id, fut)).expect("Runtime to exist");
|
||||
pub(crate) fn spawn(&self, fut: impl Future<Output = ()> + 'static) -> Task {
|
||||
let id = Runtime::with(|rt| rt.spawn(self.id, fut));
|
||||
self.spawned_tasks.borrow_mut().insert(id);
|
||||
id
|
||||
}
|
||||
|
||||
/// Queue an effect to run after the next render
|
||||
pub fn queue_effect(&self, f: impl FnOnce() + 'static) {
|
||||
Runtime::with(|rt| rt.queue_effect(self.id, f)).expect("Runtime to exist");
|
||||
pub(crate) fn queue_effect(&self, f: impl FnOnce() + 'static) {
|
||||
Runtime::with(|rt| rt.queue_effect(self.id, f));
|
||||
}
|
||||
|
||||
/// 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).
|
||||
/// `use_hook` will return a clone of the value on every render.
|
||||
///
|
||||
/// In order to clean up resources you would need to implement the [`Drop`] trait for an inner value stored in a RC or similar (Signals for instance),
|
||||
/// as these only drop their inner value once all references have been dropped, which only happens when the component is dropped.
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// `use_hook` is not reactive. It just returns the value on every render. If you need state that will track changes, use [`use_signal`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_signal.html) instead.
|
||||
///
|
||||
/// ❌ Don't use `use_hook` with `Rc<RefCell<T>>` for state. It will not update the UI and other hooks when the state changes.
|
||||
/// ```rust
|
||||
/// use dioxus::prelude::*;
|
||||
/// use std::rc::Rc;
|
||||
/// use std::cell::RefCell;
|
||||
///
|
||||
/// pub fn Comp() -> Element {
|
||||
/// let count = use_hook(|| Rc::new(RefCell::new(0)));
|
||||
///
|
||||
/// rsx! {
|
||||
/// button {
|
||||
/// onclick: move |_| *count.borrow_mut() += 1,
|
||||
/// "{count.borrow()}"
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ✅ Use `use_signal` instead.
|
||||
/// ```rust
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// pub fn Comp() -> Element {
|
||||
/// let mut count = use_signal(|| 0);
|
||||
///
|
||||
/// rsx! {
|
||||
/// button {
|
||||
/// onclick: move |_| count += 1,
|
||||
/// "{count}"
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// </div>
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// // prints a greeting on the initial render
|
||||
/// pub fn use_hello_world() {
|
||||
/// use_hook(|| println!("Hello, world!"));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Custom Hook Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// pub struct InnerCustomState(usize);
|
||||
///
|
||||
/// impl Drop for InnerCustomState {
|
||||
/// fn drop(&mut self){
|
||||
/// println!("Component has been dropped.");
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Clone, Copy)]
|
||||
/// pub struct CustomState {
|
||||
/// inner: Signal<InnerCustomState>
|
||||
/// }
|
||||
///
|
||||
/// pub fn use_custom_state() -> CustomState {
|
||||
/// use_hook(|| CustomState {
|
||||
/// inner: Signal::new(InnerCustomState(0))
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn use_hook<State: Clone + 'static>(&self, initializer: impl FnOnce() -> State) -> State {
|
||||
/// Store a value in the hook list, returning the value.
|
||||
pub(crate) fn use_hook<State: Clone + 'static>(
|
||||
&self,
|
||||
initializer: impl FnOnce() -> State,
|
||||
) -> State {
|
||||
let cur_hook = self.hook_index.get();
|
||||
|
||||
// The hook list works by keeping track of the current hook index and pushing the index forward
|
||||
@@ -513,155 +395,23 @@ impl Scope {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn push_before_render(&self, f: impl FnMut() + 'static) {
|
||||
pub(crate) fn push_before_render(&self, f: impl FnMut() + 'static) {
|
||||
self.before_render.borrow_mut().push(Box::new(f));
|
||||
}
|
||||
|
||||
pub fn push_after_render(&self, f: impl FnMut() + 'static) {
|
||||
pub(crate) fn push_after_render(&self, f: impl FnMut() + 'static) {
|
||||
self.after_render.borrow_mut().push(Box::new(f));
|
||||
}
|
||||
|
||||
/// 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) -> usize {
|
||||
pub(crate) fn generation(&self) -> usize {
|
||||
self.render_count.get()
|
||||
}
|
||||
|
||||
/// Get the height of this scope
|
||||
pub fn height(&self) -> u32 {
|
||||
pub(crate) fn height(&self) -> u32 {
|
||||
self.height
|
||||
}
|
||||
}
|
||||
|
||||
impl ScopeId {
|
||||
/// Get the current scope id
|
||||
pub fn current_scope_id(self) -> Result<ScopeId, RuntimeError> {
|
||||
Runtime::with(|rt| rt.current_scope_id().ok())
|
||||
.ok()
|
||||
.flatten()
|
||||
.ok_or(RuntimeError::new())
|
||||
}
|
||||
|
||||
/// Consume context from the current scope
|
||||
pub fn consume_context<T: 'static + Clone>(self) -> Option<T> {
|
||||
Runtime::with_scope(self, |cx| cx.consume_context::<T>()).expect("Runtime to exist")
|
||||
}
|
||||
|
||||
/// Consume context from the current scope
|
||||
pub fn consume_context_from_scope<T: 'static + Clone>(self, scope_id: ScopeId) -> Option<T> {
|
||||
Runtime::with(|rt| {
|
||||
rt.get_state(scope_id)
|
||||
.and_then(|cx| cx.consume_context::<T>())
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
/// Check if the current scope has a context
|
||||
pub fn has_context<T: 'static + Clone>(self) -> Option<T> {
|
||||
Runtime::with_scope(self, |cx| cx.has_context::<T>())
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
/// Provide context to the current scope
|
||||
pub fn provide_context<T: 'static + Clone>(self, value: T) -> T {
|
||||
Runtime::with_scope(self, |cx| cx.provide_context(value)).unwrap()
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
Runtime::with_scope(self, |cx| cx.spawn(fut)).ok()
|
||||
}
|
||||
|
||||
/// Spawns the future but does not return the [`Task`]
|
||||
pub fn spawn(self, fut: impl Future<Output = ()> + 'static) {
|
||||
Runtime::with_scope(self, |cx| cx.spawn(fut)).unwrap();
|
||||
}
|
||||
|
||||
/// 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<usize> {
|
||||
Runtime::with_scope(self, |cx| Some(cx.generation())).unwrap()
|
||||
}
|
||||
|
||||
/// Get the parent of the current scope if it exists
|
||||
pub fn parent_scope(self) -> Option<ScopeId> {
|
||||
Runtime::with_scope(self, |cx| cx.parent_id())
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
/// Check if the current scope is a descendant of the given scope
|
||||
pub fn is_descendant_of(self, other: ScopeId) -> bool {
|
||||
let mut current = self;
|
||||
while let Some(parent) = current.parent_scope() {
|
||||
if parent == other {
|
||||
return true;
|
||||
}
|
||||
current = parent;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Mark the current scope as dirty, causing it to re-render
|
||||
pub fn needs_update(self) {
|
||||
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.
|
||||
///
|
||||
/// ## Notice: you should prefer using [`crate::schedule_update_any`]
|
||||
pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
|
||||
Runtime::with_scope(*self, |cx| cx.schedule_update()).unwrap()
|
||||
}
|
||||
|
||||
/// Get the height of the current scope
|
||||
pub fn height(self) -> u32 {
|
||||
Runtime::with_scope(self, |cx| cx.height()).unwrap()
|
||||
}
|
||||
|
||||
/// Run a closure inside of scope's runtime
|
||||
#[track_caller]
|
||||
pub fn in_runtime<T>(self, f: impl FnOnce() -> T) -> T {
|
||||
Runtime::current()
|
||||
.unwrap_or_else(|e| panic!("{}", e))
|
||||
.on_scope(self, f)
|
||||
}
|
||||
|
||||
/// Throw a [`CapturedError`] into a scope. The error will bubble up to the nearest [`ErrorBoundary`](crate::ErrorBoundary) or the root of the app.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// fn Component() -> Element {
|
||||
/// let request = spawn(async move {
|
||||
/// match reqwest::get("https://api.example.com").await {
|
||||
/// Ok(_) => unimplemented!(),
|
||||
/// // You can explicitly throw an error into a scope with throw_error
|
||||
/// Err(err) => ScopeId::APP.throw_error(err)
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
/// ```
|
||||
pub fn throw_error(self, error: impl Into<CapturedError> + 'static) {
|
||||
let error = error.into();
|
||||
if let Some(cx) = self.consume_context::<crate::ErrorContext>() {
|
||||
cx.insert_error(error)
|
||||
} else {
|
||||
tracing::error!(
|
||||
"Tried to throw an error into an error boundary, but failed to locate a boundary: {:?}",
|
||||
error
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the suspense context the current scope is in
|
||||
pub fn suspense_context(&self) -> Option<SuspenseContext> {
|
||||
Runtime::with_scope(*self, |cx| cx.suspense_boundary.suspense_context().cloned()).unwrap()
|
||||
}
|
||||
}
|
||||
+42
-38
@@ -20,12 +20,11 @@ impl std::fmt::Debug for ScopeId {
|
||||
let mut builder = builder.field(&self.0);
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if let Some(name) = Runtime::current()
|
||||
.ok()
|
||||
if let Some(scope) = Runtime::try_current()
|
||||
.as_ref()
|
||||
.and_then(|rt| rt.get_state(*self))
|
||||
.and_then(|r| r.try_get_state(*self))
|
||||
{
|
||||
builder = builder.field(&name.name);
|
||||
builder = builder.field(&scope.name);
|
||||
}
|
||||
}
|
||||
builder.finish()
|
||||
@@ -81,6 +80,45 @@ pub struct ScopeState {
|
||||
pub(crate) reactive_context: ReactiveContext,
|
||||
}
|
||||
|
||||
impl ScopeState {
|
||||
/// Get a handle to the currently active head node arena for this Scope
|
||||
///
|
||||
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
|
||||
///
|
||||
/// Panics if the tree has not been built yet.
|
||||
pub fn root_node(&self) -> &VNode {
|
||||
self.try_root_node()
|
||||
.expect("The tree has not been built yet. Make sure to call rebuild on the tree before accessing its nodes.")
|
||||
}
|
||||
|
||||
/// Try to get a handle to the currently active head node arena for this Scope
|
||||
///
|
||||
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
|
||||
///
|
||||
/// Returns [`None`] if the tree has not been built yet.
|
||||
pub fn try_root_node(&self) -> Option<&VNode> {
|
||||
match &self.last_rendered_node {
|
||||
Some(LastRenderedNode::Real(vnode)) => Some(vnode),
|
||||
Some(LastRenderedNode::Placeholder(vnode, _)) => Some(vnode),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the scope id of this [`ScopeState`].
|
||||
pub fn id(&self) -> ScopeId {
|
||||
self.context_id
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> Ref<'_, Scope> {
|
||||
self.runtime.get_state(self.context_id)
|
||||
}
|
||||
|
||||
/// Returns the height of this scope in the tree.
|
||||
pub fn height(&self) -> u32 {
|
||||
self.state().height()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum LastRenderedNode {
|
||||
Real(VNode),
|
||||
@@ -119,37 +157,3 @@ impl Drop for ScopeState {
|
||||
self.runtime.remove_scope(self.context_id);
|
||||
}
|
||||
}
|
||||
|
||||
impl ScopeState {
|
||||
/// Get a handle to the currently active head node arena for this Scope
|
||||
///
|
||||
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
|
||||
///
|
||||
/// Panics if the tree has not been built yet.
|
||||
pub fn root_node(&self) -> &VNode {
|
||||
self.try_root_node()
|
||||
.expect("The tree has not been built yet. Make sure to call rebuild on the tree before accessing its nodes.")
|
||||
}
|
||||
|
||||
/// Try to get a handle to the currently active head node arena for this Scope
|
||||
///
|
||||
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
|
||||
///
|
||||
/// Returns [`None`] if the tree has not been built yet.
|
||||
pub fn try_root_node(&self) -> Option<&VNode> {
|
||||
match &self.last_rendered_node {
|
||||
Some(LastRenderedNode::Real(vnode)) => Some(vnode),
|
||||
Some(LastRenderedNode::Placeholder(vnode, _)) => Some(vnode),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the scope id of this [`ScopeState`].
|
||||
pub fn id(&self) -> ScopeId {
|
||||
self.context_id
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> Ref<'_, Scope> {
|
||||
self.runtime.get_state(self.context_id).unwrap()
|
||||
}
|
||||
}
|
||||
@@ -607,7 +607,7 @@ impl SuspenseContext {
|
||||
scope_id: ScopeId,
|
||||
) -> Option<Self> {
|
||||
runtime
|
||||
.get_state(scope_id)
|
||||
.try_get_state(scope_id)
|
||||
.and_then(|scope| scope.suspense_boundary())
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ impl SuspendedFuture {
|
||||
pub fn new(task: Task) -> Self {
|
||||
Self {
|
||||
task: task.id,
|
||||
origin: current_scope_id().unwrap_or_else(|e| panic!("{}", e)),
|
||||
origin: current_scope_id(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ impl SuspenseContext {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
inner: Rc::new(SuspenseBoundaryInner {
|
||||
rt: Runtime::current(),
|
||||
suspended_tasks: RefCell::new(vec![]),
|
||||
id: Cell::new(ScopeId::ROOT),
|
||||
suspended_nodes: Default::default(),
|
||||
@@ -145,7 +146,7 @@ impl SuspenseContext {
|
||||
/// Add a suspended task
|
||||
pub(crate) fn add_suspended_task(&self, task: SuspendedFuture) {
|
||||
self.inner.suspended_tasks.borrow_mut().push(task);
|
||||
self.inner.id.get().needs_update();
|
||||
self.inner.rt.needs_update(self.inner.id.get());
|
||||
}
|
||||
|
||||
/// Remove a suspended task
|
||||
@@ -154,7 +155,7 @@ impl SuspenseContext {
|
||||
.suspended_tasks
|
||||
.borrow_mut()
|
||||
.retain(|t| t.task != task.id);
|
||||
self.inner.id.get().needs_update();
|
||||
self.inner.rt.needs_update(self.inner.id.get());
|
||||
}
|
||||
|
||||
/// Get all suspended tasks
|
||||
@@ -184,12 +185,18 @@ impl SuspenseContext {
|
||||
|
||||
/// A boundary that will capture any errors from child components
|
||||
pub struct SuspenseBoundaryInner {
|
||||
rt: Rc<Runtime>,
|
||||
|
||||
suspended_tasks: RefCell<Vec<SuspendedFuture>>,
|
||||
|
||||
id: Cell<ScopeId>,
|
||||
|
||||
/// The nodes that are suspended under this boundary
|
||||
suspended_nodes: RefCell<Option<VNode>>,
|
||||
|
||||
/// On the server, you can only resolve a suspense boundary once. This is used to track if the suspense boundary has been resolved and if it should be frozen
|
||||
frozen: Cell<bool>,
|
||||
|
||||
/// Closures queued to run after the suspense boundary is resolved
|
||||
after_suspense_resolved: RefCell<Vec<Box<dyn FnOnce()>>>,
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use crate::ScopeId;
|
||||
use futures_util::task::ArcWake;
|
||||
use slotmap::DefaultKey;
|
||||
use std::marker::PhantomData;
|
||||
use std::panic;
|
||||
use std::sync::Arc;
|
||||
use std::task::Waker;
|
||||
use std::{cell::Cell, future::Future};
|
||||
@@ -73,7 +72,6 @@ impl Task {
|
||||
false
|
||||
}
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Wake the task.
|
||||
@@ -84,13 +82,12 @@ impl Task {
|
||||
.sender
|
||||
.unbounded_send(SchedulerMsg::TaskNotified(self.id))
|
||||
})
|
||||
.unwrap_or_else(|e| panic!("{}", e))
|
||||
}
|
||||
|
||||
/// Poll the task immediately.
|
||||
#[track_caller]
|
||||
pub fn poll_now(&self) -> Poll<()> {
|
||||
Runtime::with(|rt| rt.handle_task_wakeup(*self)).unwrap_or_else(|e| panic!("{}", e))
|
||||
Runtime::with(|rt| rt.handle_task_wakeup(*self))
|
||||
}
|
||||
|
||||
/// Set the task as active or paused.
|
||||
@@ -106,7 +103,6 @@ impl Task {
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|e| panic!("{}", e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +213,7 @@ impl Runtime {
|
||||
/// Queue an effect to run after the next render
|
||||
pub(crate) fn queue_effect(&self, id: ScopeId, f: impl FnOnce() + 'static) {
|
||||
let effect = Box::new(f) as Box<dyn FnOnce() + 'static>;
|
||||
let Some(scope) = self.get_state(id) else {
|
||||
let Some(scope) = self.try_get_state(id) else {
|
||||
return;
|
||||
};
|
||||
let mut status = scope.status.borrow_mut();
|
||||
@@ -239,7 +235,8 @@ impl Runtime {
|
||||
) {
|
||||
// Add the effect to the queue of effects to run after the next render for the given scope
|
||||
let mut effects = self.pending_effects.borrow_mut();
|
||||
let scope_order = ScopeOrder::new(id.height(), id);
|
||||
let height = self.get_state(id).height();
|
||||
let scope_order = ScopeOrder::new(height, id);
|
||||
match effects.get(&scope_order) {
|
||||
Some(effects) => effects.push_back(f),
|
||||
None => {
|
||||
@@ -267,7 +264,7 @@ impl Runtime {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
// Ensure we are currently inside a `Runtime`.
|
||||
Runtime::current().unwrap_or_else(|e| panic!("{}", e));
|
||||
Runtime::current();
|
||||
}
|
||||
|
||||
let task = self.tasks.borrow().get(id.id).cloned();
|
||||
@@ -293,7 +290,6 @@ impl Runtime {
|
||||
if poll_result.is_ready() {
|
||||
// Remove it from the scope so we dont try to double drop it when the scope dropes
|
||||
self.get_state(task.scope)
|
||||
.unwrap()
|
||||
.spawned_tasks
|
||||
.borrow_mut()
|
||||
.remove(&id);
|
||||
@@ -325,7 +321,7 @@ impl Runtime {
|
||||
}
|
||||
|
||||
// Remove the task from pending work. We could reuse the slot before the task is polled and discarded so we need to remove it from pending work instead of filtering out dead tasks when we try to poll them
|
||||
if let Some(scope) = self.get_state(task.scope) {
|
||||
if let Some(scope) = self.try_get_state(task.scope) {
|
||||
let order = ScopeOrder::new(scope.height(), scope.id);
|
||||
if let Some(dirty_tasks) = self.dirty_tasks.borrow_mut().get(&order) {
|
||||
dirty_tasks.remove(id);
|
||||
|
||||
@@ -353,7 +353,7 @@ impl VirtualDom {
|
||||
|
||||
/// Run a closure inside a specific scope
|
||||
pub fn in_scope<T>(&self, scope: ScopeId, f: impl FnOnce() -> T) -> T {
|
||||
self.in_runtime(|| scope.in_runtime(f))
|
||||
self.runtime.in_scope(scope, f)
|
||||
}
|
||||
|
||||
/// Build the virtualdom with a global context inserted into the base scope
|
||||
@@ -393,7 +393,7 @@ impl VirtualDom {
|
||||
///
|
||||
/// Whenever the Runtime "works", it will re-render this scope
|
||||
pub fn mark_dirty(&mut self, id: ScopeId) {
|
||||
let Some(scope) = self.runtime.get_state(id) else {
|
||||
let Some(scope) = self.runtime.try_get_state(id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -408,7 +408,7 @@ impl VirtualDom {
|
||||
let Some(scope) = self.runtime.task_scope(task) else {
|
||||
return;
|
||||
};
|
||||
let Some(scope) = self.runtime.get_state(scope) else {
|
||||
let Some(scope) = self.runtime.try_get_state(scope) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -718,7 +718,7 @@ impl VirtualDom {
|
||||
let scope_id: ScopeId = scope.id;
|
||||
let run_scope = self
|
||||
.runtime
|
||||
.get_state(scope.id)
|
||||
.try_get_state(scope.id)
|
||||
.filter(|scope| scope.should_run_during_suspense())
|
||||
.is_some();
|
||||
if run_scope {
|
||||
@@ -747,7 +747,7 @@ impl VirtualDom {
|
||||
}
|
||||
|
||||
self.resolved_scopes
|
||||
.sort_by_key(|&id| self.runtime.get_state(id).unwrap().height);
|
||||
.sort_by_key(|&id| self.runtime.get_state(id).height);
|
||||
std::mem::take(&mut self.resolved_scopes)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use dioxus::dioxus_core::{ElementId, Mutation::*};
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_core::generation;
|
||||
use dioxus_core::{consume_context_from_scope, generation};
|
||||
|
||||
#[test]
|
||||
fn state_shares() {
|
||||
@@ -31,13 +31,13 @@ fn state_shares() {
|
||||
dom.mark_dirty(ScopeId::APP);
|
||||
_ = dom.render_immediate_to_vec();
|
||||
dom.in_runtime(|| {
|
||||
assert_eq!(ScopeId::APP.consume_context::<i32>().unwrap(), 1);
|
||||
assert_eq!(consume_context_from_scope::<i32>(ScopeId::APP).unwrap(), 1);
|
||||
});
|
||||
|
||||
dom.mark_dirty(ScopeId::APP);
|
||||
_ = dom.render_immediate_to_vec();
|
||||
dom.in_runtime(|| {
|
||||
assert_eq!(ScopeId::APP.consume_context::<i32>().unwrap(), 2);
|
||||
assert_eq!(consume_context_from_scope::<i32>(ScopeId::APP).unwrap(), 2);
|
||||
});
|
||||
|
||||
dom.mark_dirty(ScopeId(ScopeId::APP.0 + 2));
|
||||
|
||||
@@ -14,6 +14,6 @@ fn root_node_isnt_null() {
|
||||
|
||||
dom.in_runtime(|| {
|
||||
// The height should be 0
|
||||
assert_eq!(ScopeId::ROOT.height(), 0);
|
||||
assert_eq!(scope.height(), 0);
|
||||
});
|
||||
}
|
||||
@@ -272,7 +272,7 @@ fn resolved_to_suspended() {
|
||||
|
||||
assert_eq!(out, "rendered 1 times");
|
||||
|
||||
dom.in_runtime(|| ScopeId::APP.in_runtime(|| *SUSPENDED.write() = true));
|
||||
dom.in_scope(ScopeId::APP, || *SUSPENDED.write() = true);
|
||||
|
||||
dom.render_suspense_immediate().await;
|
||||
let out = dioxus_ssr::render(&dom);
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
shortcut::ShortcutRegistry,
|
||||
webview::{PendingWebview, WebviewInstance},
|
||||
};
|
||||
use dioxus_core::{ElementId, ScopeId, VirtualDom};
|
||||
use dioxus_core::{consume_context, ElementId, ScopeId, VirtualDom};
|
||||
use dioxus_history::History;
|
||||
use dioxus_html::PlatformEventData;
|
||||
use std::{
|
||||
@@ -550,11 +550,8 @@ impl App {
|
||||
return;
|
||||
};
|
||||
|
||||
let url = webview.dom.in_runtime(|| {
|
||||
ScopeId::ROOT
|
||||
.consume_context::<Rc<dyn History>>()
|
||||
.unwrap()
|
||||
.current_route()
|
||||
let url = webview.dom.in_scope(ScopeId::ROOT, || {
|
||||
consume_context::<Rc<dyn History>>().current_route()
|
||||
});
|
||||
|
||||
let state = PreservedWindowState {
|
||||
@@ -619,12 +616,9 @@ impl App {
|
||||
}
|
||||
|
||||
// Set the url if it exists
|
||||
webview.dom.in_runtime(|| {
|
||||
webview.dom.in_scope(ScopeId::ROOT, || {
|
||||
if let Some(url) = state.url {
|
||||
ScopeId::ROOT
|
||||
.consume_context::<Rc<dyn History>>()
|
||||
.unwrap()
|
||||
.replace(url);
|
||||
consume_context::<Rc<dyn History>>().replace(url);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -22,13 +22,13 @@ pub fn use_wry_event_handler(
|
||||
use dioxus_core::current_scope_id;
|
||||
|
||||
// Capture the current runtime and scope ID.
|
||||
let runtime = Runtime::current().unwrap();
|
||||
let scope_id = current_scope_id().unwrap();
|
||||
let runtime = Runtime::current();
|
||||
let scope_id = current_scope_id();
|
||||
|
||||
use_hook_with_cleanup(
|
||||
move || {
|
||||
window().create_wry_event_handler(move |event, target| {
|
||||
runtime.on_scope(scope_id, || handler(event, target))
|
||||
runtime.in_scope(scope_id, || handler(event, target))
|
||||
})
|
||||
},
|
||||
move |handler| handler.remove(),
|
||||
|
||||
@@ -60,37 +60,31 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, mut desktop_config:
|
||||
// Windows-only drag-n-drop fix events. We need to call the interpreter drag-n-drop code.
|
||||
UserWindowEvent::WindowsDragDrop(id) => {
|
||||
if let Some(webview) = app.webviews.get(&id) {
|
||||
webview.dom.in_runtime(|| {
|
||||
ScopeId::ROOT.in_runtime(|| {
|
||||
eval("window.interpreter.handleWindowsDragDrop();");
|
||||
});
|
||||
webview.dom.in_scope(ScopeId::ROOT, || {
|
||||
eval("window.interpreter.handleWindowsDragDrop();");
|
||||
});
|
||||
}
|
||||
}
|
||||
UserWindowEvent::WindowsDragLeave(id) => {
|
||||
if let Some(webview) = app.webviews.get(&id) {
|
||||
webview.dom.in_runtime(|| {
|
||||
ScopeId::ROOT.in_runtime(|| {
|
||||
eval("window.interpreter.handleWindowsDragLeave();");
|
||||
});
|
||||
webview.dom.in_scope(ScopeId::ROOT, || {
|
||||
eval("window.interpreter.handleWindowsDragLeave();");
|
||||
});
|
||||
}
|
||||
}
|
||||
UserWindowEvent::WindowsDragOver(id, x_pos, y_pos) => {
|
||||
if let Some(webview) = app.webviews.get(&id) {
|
||||
webview.dom.in_runtime(|| {
|
||||
ScopeId::ROOT.in_runtime(|| {
|
||||
let e = eval(
|
||||
r#"
|
||||
webview.dom.in_scope(ScopeId::ROOT, || {
|
||||
let e = eval(
|
||||
r#"
|
||||
const xPos = await dioxus.recv();
|
||||
const yPos = await dioxus.recv();
|
||||
window.interpreter.handleWindowsDragOver(xPos, yPos)
|
||||
"#,
|
||||
);
|
||||
);
|
||||
|
||||
_ = e.send(x_pos);
|
||||
_ = e.send(y_pos);
|
||||
});
|
||||
_ = e.send(x_pos);
|
||||
_ = e.send(y_pos);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
};
|
||||
use crate::{document::DesktopDocument, WeakDesktopContext};
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
use dioxus_core::{Runtime, ScopeId, VirtualDom};
|
||||
use dioxus_core::{consume_context, provide_context, Runtime, ScopeId, VirtualDom};
|
||||
use dioxus_document::Document;
|
||||
use dioxus_history::{History, MemoryHistory};
|
||||
use dioxus_hooks::to_owned;
|
||||
@@ -427,10 +427,10 @@ impl WebviewInstance {
|
||||
edits.set_desktop_context(Rc::downgrade(&desktop_context));
|
||||
let provider: Rc<dyn Document> = Rc::new(DesktopDocument::new(desktop_context.clone()));
|
||||
let history_provider: Rc<dyn History> = Rc::new(MemoryHistory::default());
|
||||
dom.in_runtime(|| {
|
||||
ScopeId::ROOT.provide_context(desktop_context.clone());
|
||||
ScopeId::ROOT.provide_context(provider);
|
||||
ScopeId::ROOT.provide_context(history_provider);
|
||||
dom.in_scope(ScopeId::ROOT, || {
|
||||
provide_context(desktop_context.clone());
|
||||
provide_context(provider);
|
||||
provide_context(history_provider);
|
||||
});
|
||||
|
||||
WebviewInstance {
|
||||
@@ -566,11 +566,9 @@ impl PendingWebview {
|
||||
pub(crate) fn create_window(self, shared: &Rc<SharedContext>) -> WebviewInstance {
|
||||
let window = WebviewInstance::new(self.cfg, self.dom, shared.clone());
|
||||
|
||||
let cx = window.dom.in_runtime(|| {
|
||||
ScopeId::ROOT
|
||||
.consume_context::<Rc<DesktopService>>()
|
||||
.unwrap()
|
||||
});
|
||||
let cx = window
|
||||
.dom
|
||||
.in_scope(ScopeId::ROOT, consume_context::<Rc<DesktopService>>);
|
||||
_ = self.sender.send(cx);
|
||||
|
||||
window
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) {
|
||||
///
|
||||
/// Assets need to be handled by the renderer.
|
||||
pub fn try_apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) -> Result<(), PatchError> {
|
||||
dom.runtime().on_scope(ScopeId::ROOT, || {
|
||||
dom.runtime().in_scope(ScopeId::ROOT, || {
|
||||
// 1. Update signals...
|
||||
let ctx = dioxus_signals::get_global_context();
|
||||
for template in &msg.templates {
|
||||
@@ -44,7 +44,7 @@ pub fn try_apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) -> Result<(), Pat
|
||||
|
||||
if msg.for_pid == our_pid {
|
||||
unsafe { subsecond::apply_patch(jump_table) }?;
|
||||
dioxus_core::force_all_dirty();
|
||||
dom.runtime().force_all_dirty();
|
||||
ctx.clear::<Signal<Option<HotReloadedTemplate>>>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
use std::{cell::RefCell, collections::HashSet, rc::Rc};
|
||||
|
||||
use dioxus_core::{Attribute, DynamicNode, Element, RenderError, ScopeId, Template, TemplateNode};
|
||||
use dioxus_core::{
|
||||
Attribute, DynamicNode, Element, RenderError, Runtime, ScopeId, Template, TemplateNode,
|
||||
};
|
||||
use dioxus_core_macro::*;
|
||||
|
||||
mod link;
|
||||
@@ -101,11 +103,12 @@ fn extract_single_text_node(children: &Element) -> Result<String, ExtractSingleT
|
||||
}
|
||||
|
||||
fn get_or_insert_root_context<T: Default + Clone + 'static>() -> T {
|
||||
match ScopeId::ROOT.has_context::<T>() {
|
||||
let rt = Runtime::current();
|
||||
match rt.has_context::<T>(ScopeId::ROOT) {
|
||||
Some(context) => context,
|
||||
None => {
|
||||
let context = T::default();
|
||||
ScopeId::ROOT.provide_context(context.clone());
|
||||
rt.provide_context(ScopeId::ROOT, context.clone());
|
||||
context
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ use crate::streaming::{Mount, StreamingRenderer};
|
||||
use crate::{document::ServerDocument, ServeConfig};
|
||||
use dioxus_cli_config::base_path;
|
||||
use dioxus_core::{
|
||||
has_context, DynamicNode, ErrorContext, ScopeId, SuspenseContext, TemplateNode, VNode,
|
||||
VirtualDom,
|
||||
consume_context, has_context, try_consume_context, DynamicNode, ErrorContext, Runtime, ScopeId,
|
||||
SuspenseContext, TemplateNode, VNode, VirtualDom,
|
||||
};
|
||||
use dioxus_fullstack_core::{history::provide_fullstack_history_context, HttpError, ServerFnError};
|
||||
use dioxus_fullstack_core::{HydrationContext, SerializedHydrationData};
|
||||
@@ -219,11 +219,8 @@ impl SsrRendererPool {
|
||||
}
|
||||
|
||||
// check if there are any errors
|
||||
let error = virtual_dom.in_runtime(|| {
|
||||
ScopeId::ROOT_ERROR_BOUNDARY
|
||||
.consume_context::<ErrorContext>()
|
||||
.expect("The root should be under an error boundary")
|
||||
.error()
|
||||
let error = virtual_dom.in_scope(ScopeId::ROOT_ERROR_BOUNDARY, || {
|
||||
consume_context::<ErrorContext>().error()
|
||||
});
|
||||
|
||||
if let Some(error) = error {
|
||||
@@ -497,7 +494,9 @@ impl SsrRendererPool {
|
||||
fn start_capturing_errors(suspense_scope: ScopeId) {
|
||||
// Add an error boundary to the scope. We serialize the suspense error boundary separately so we can use
|
||||
// the normal in memory ErrorContext here
|
||||
suspense_scope.in_runtime(|| dioxus_core::provide_context(ErrorContext::new(None)));
|
||||
Runtime::current().in_scope(suspense_scope, || {
|
||||
dioxus_core::provide_context(ErrorContext::new(None))
|
||||
});
|
||||
}
|
||||
|
||||
fn serialize_server_data(virtual_dom: &VirtualDom, scope: ScopeId) -> SerializedHydrationData {
|
||||
@@ -525,10 +524,8 @@ impl SsrRendererPool {
|
||||
fn serialize_errors(context: &HydrationContext, vdom: &VirtualDom, scope: ScopeId) {
|
||||
// If there is an error boundary on the suspense boundary, grab the error from the context API
|
||||
// and throw it on the client so that it bubbles up to the nearest error boundary
|
||||
let error = vdom.in_runtime(|| {
|
||||
scope
|
||||
.consume_context::<ErrorContext>()
|
||||
.and_then(|error_context| error_context.error())
|
||||
let error = vdom.in_scope(scope, || {
|
||||
try_consume_context::<ErrorContext>().and_then(|error_context| error_context.error())
|
||||
});
|
||||
context
|
||||
.error_entry()
|
||||
@@ -536,14 +533,12 @@ impl SsrRendererPool {
|
||||
}
|
||||
|
||||
fn take_from_scope(context: &HydrationContext, vdom: &VirtualDom, scope: ScopeId) {
|
||||
vdom.in_runtime(|| {
|
||||
scope.in_runtime(|| {
|
||||
// Grab any serializable server context from this scope
|
||||
let other: Option<HydrationContext> = has_context();
|
||||
if let Some(other) = other {
|
||||
context.extend(&other);
|
||||
}
|
||||
});
|
||||
vdom.in_scope(scope, || {
|
||||
// Grab any serializable server context from this scope
|
||||
let other: Option<HydrationContext> = has_context();
|
||||
if let Some(other) = other {
|
||||
context.extend(&other);
|
||||
}
|
||||
});
|
||||
|
||||
// then continue to any children
|
||||
@@ -659,7 +654,7 @@ impl SsrRendererPool {
|
||||
|
||||
let title = {
|
||||
let document: Option<Rc<ServerDocument>> =
|
||||
virtual_dom.in_runtime(|| ScopeId::ROOT.consume_context());
|
||||
virtual_dom.in_scope(ScopeId::ROOT, dioxus_core::try_consume_context);
|
||||
// Collect any head content from the document provider and inject that into the head
|
||||
document.and_then(|document| document.title())
|
||||
};
|
||||
@@ -672,8 +667,8 @@ impl SsrRendererPool {
|
||||
}
|
||||
to.write_str(&index.head_after_title)?;
|
||||
|
||||
let document: Option<Rc<ServerDocument>> =
|
||||
virtual_dom.in_runtime(|| ScopeId::ROOT.consume_context());
|
||||
let document =
|
||||
virtual_dom.in_scope(ScopeId::ROOT, try_consume_context::<Rc<ServerDocument>>);
|
||||
if let Some(document) = document {
|
||||
// Collect any head content from the document provider and inject that into the head
|
||||
document.render(to)?;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use dioxus_core::{suspense_context, use_hook};
|
||||
use dioxus_core::{use_hook, Runtime};
|
||||
|
||||
/// Run a closure after the suspense boundary this is under is resolved. The
|
||||
/// closure will be run immediately if the suspense boundary is already resolved
|
||||
@@ -6,7 +6,7 @@ use dioxus_core::{suspense_context, use_hook};
|
||||
pub fn use_after_suspense_resolved(suspense_resolved: impl FnOnce() + 'static) {
|
||||
use_hook(|| {
|
||||
// If this is under a suspense boundary, we need to check if it is resolved
|
||||
match suspense_context() {
|
||||
match Runtime::current().suspense_context() {
|
||||
Some(context) => {
|
||||
// If it is suspended, run the closure after the suspense is resolved
|
||||
context.after_suspense_resolved(suspense_resolved)
|
||||
|
||||
@@ -64,18 +64,19 @@ impl Evaluator for LiveviewEvaluator {
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides the LiveviewDocument through [`ScopeId::provide_context`].
|
||||
/// Provides the LiveviewDocument through [`dioxus_core::Runtime::provide_context`].
|
||||
pub fn init_document() {
|
||||
let query = ScopeId::ROOT.consume_context::<QueryEngine>().unwrap();
|
||||
let rt = dioxus_core::Runtime::current();
|
||||
let query = rt.consume_context::<QueryEngine>(ScopeId::ROOT).unwrap();
|
||||
let provider: Rc<dyn Document> = Rc::new(LiveviewDocument {
|
||||
query: query.clone(),
|
||||
});
|
||||
ScopeId::ROOT.provide_context(provider);
|
||||
rt.provide_context(ScopeId::ROOT, provider);
|
||||
let history = LiveviewHistory::new(Rc::new(move |script: &str| {
|
||||
Eval::new(LiveviewEvaluator::create(query.clone(), script.to_string()))
|
||||
}));
|
||||
let history: Rc<dyn History> = Rc::new(history);
|
||||
ScopeId::ROOT.provide_context(history);
|
||||
rt.provide_context(ScopeId::ROOT, history);
|
||||
}
|
||||
|
||||
/// Reprints the liveview-target's provider of evaluators.
|
||||
|
||||
@@ -133,7 +133,7 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li
|
||||
// Create the a proxy for query engine
|
||||
let (query_tx, mut query_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let query_engine = QueryEngine::new(query_tx);
|
||||
vdom.runtime().on_scope(ScopeId::ROOT, || {
|
||||
vdom.runtime().in_scope(ScopeId::ROOT, || {
|
||||
provide_context(query_engine.clone());
|
||||
init_document();
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use blitz_shell::{BlitzApplication, View};
|
||||
use dioxus_core::ScopeId;
|
||||
use dioxus_core::{provide_context, ScopeId};
|
||||
use dioxus_history::{History, MemoryHistory};
|
||||
use std::rc::Rc;
|
||||
use winit::application::ApplicationHandler;
|
||||
@@ -120,20 +120,20 @@ impl ApplicationHandler<BlitzShellEvent> for DioxusNativeApplication {
|
||||
let window_id = window.window_id();
|
||||
let doc = window.downcast_doc_mut::<DioxusDocument>();
|
||||
|
||||
doc.vdom.in_runtime(|| {
|
||||
doc.vdom.in_scope(ScopeId::ROOT, || {
|
||||
let shared: Rc<dyn dioxus_document::Document> =
|
||||
Rc::new(DioxusNativeDocument::new(self.proxy.clone(), window_id));
|
||||
ScopeId::ROOT.provide_context(shared);
|
||||
provide_context(shared);
|
||||
});
|
||||
|
||||
// Add history
|
||||
let history_provider: Rc<dyn History> = Rc::new(MemoryHistory::default());
|
||||
doc.vdom
|
||||
.in_runtime(move || ScopeId::ROOT.provide_context(history_provider));
|
||||
.in_scope(ScopeId::ROOT, move || provide_context(history_provider));
|
||||
|
||||
// Add renderer
|
||||
doc.vdom
|
||||
.in_runtime(move || ScopeId::ROOT.provide_context(renderer));
|
||||
.in_scope(ScopeId::ROOT, move || provide_context(renderer));
|
||||
|
||||
// Queue rebuild
|
||||
doc.initial_build();
|
||||
|
||||
@@ -115,7 +115,7 @@ fn WebSysClosure() -> Element {
|
||||
|
||||
// Make sure passing the runtime into the closure works
|
||||
let callback = Callback::new(|_| {
|
||||
assert!(!dioxus::dioxus_core::vdom_is_rendering());
|
||||
assert!(!dioxus::dioxus_core::Runtime::current().vdom_is_rendering());
|
||||
*TRIGGERED.write() = true;
|
||||
});
|
||||
let closure: Closure<dyn Fn()> = Closure::new({
|
||||
|
||||
@@ -35,20 +35,25 @@ struct RootRouterContext(Signal<Option<RouterContext>>);
|
||||
///
|
||||
/// This will return `None` if there is no router present or the router has not been created yet.
|
||||
pub fn root_router() -> Option<RouterContext> {
|
||||
if let Some(ctx) = ScopeId::ROOT.consume_context::<RootRouterContext>() {
|
||||
let rt = dioxus_core::Runtime::current();
|
||||
|
||||
if let Some(ctx) = rt.consume_context::<RootRouterContext>(ScopeId::ROOT) {
|
||||
ctx.0.cloned()
|
||||
} else {
|
||||
ScopeId::ROOT.provide_context(RootRouterContext(Signal::new_in_scope(None, ScopeId::ROOT)));
|
||||
rt.provide_context(
|
||||
ScopeId::ROOT,
|
||||
RootRouterContext(Signal::new_in_scope(None, ScopeId::ROOT)),
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn provide_router_context(ctx: RouterContext) {
|
||||
if root_router().is_none() {
|
||||
ScopeId::ROOT.provide_context(RootRouterContext(Signal::new_in_scope(
|
||||
Some(ctx),
|
||||
dioxus_core::Runtime::current().provide_context(
|
||||
ScopeId::ROOT,
|
||||
)));
|
||||
RootRouterContext(Signal::new_in_scope(Some(ctx), ScopeId::ROOT)),
|
||||
);
|
||||
}
|
||||
provide_context(ctx);
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ impl ToTokens for TemplateBody {
|
||||
#index
|
||||
);
|
||||
|
||||
dioxus_core::Runtime::current().ok().map(|_| __TEMPLATE.read())
|
||||
dioxus_core::Runtime::try_current().map(|_| __TEMPLATE.read())
|
||||
};
|
||||
// If the template has not been hot reloaded, we always use the original template
|
||||
// Templates nested within macros may be merged because they have the same file-line-column-index
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#![allow(clippy::unnecessary_operation)]
|
||||
#![allow(clippy::no_effect)]
|
||||
|
||||
use dioxus_core::Subscribers;
|
||||
use dioxus_core::{current_owner, current_scope_id, ScopeId};
|
||||
use dioxus_core::{Runtime, Subscribers};
|
||||
use generational_box::{
|
||||
AnyStorage, BorrowResult, GenerationalBox, GenerationalBoxId, Storage, UnsyncStorage,
|
||||
};
|
||||
@@ -82,7 +82,7 @@ impl<T, S: Storage<T>> CopyValue<T, S> {
|
||||
{
|
||||
Self {
|
||||
value: GenerationalBox::leak(value, caller),
|
||||
origin_scope: current_scope_id().expect("in a virtual dom"),
|
||||
origin_scope: current_scope_id(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ impl<T, S: Storage<T>> CopyValue<T, S> {
|
||||
|
||||
Self {
|
||||
value: owner.insert_rc_with_caller(value, caller),
|
||||
origin_scope: current_scope_id().expect("in a virtual dom"),
|
||||
origin_scope: current_scope_id(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,8 +116,7 @@ impl<T, S: Storage<T>> CopyValue<T, S> {
|
||||
scope: ScopeId,
|
||||
caller: &'static std::panic::Location<'static>,
|
||||
) -> Self {
|
||||
let owner = scope.owner();
|
||||
|
||||
let owner = Runtime::current().scope_owner(scope);
|
||||
Self {
|
||||
value: owner.insert_rc_with_caller(value, caller),
|
||||
origin_scope: scope,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use dioxus_core::{ScopeId, Subscribers};
|
||||
use dioxus_core::{Runtime, ScopeId, Subscribers};
|
||||
use generational_box::BorrowResult;
|
||||
use std::{any::Any, cell::RefCell, collections::HashMap, ops::Deref, panic::Location, rc::Rc};
|
||||
|
||||
@@ -190,7 +190,9 @@ where
|
||||
}
|
||||
// Otherwise, create it
|
||||
// Constructors are always run in the root scope
|
||||
let signal = ScopeId::ROOT.in_runtime(|| T::initialize_from_function(self.constructor));
|
||||
let signal = dioxus_core::Runtime::current().in_scope(ScopeId::ROOT, || {
|
||||
T::initialize_from_function(self.constructor)
|
||||
});
|
||||
context
|
||||
.map
|
||||
.borrow_mut()
|
||||
@@ -274,9 +276,10 @@ impl GlobalLazyContext {
|
||||
|
||||
/// Get the global context for signals
|
||||
pub fn get_global_context() -> GlobalLazyContext {
|
||||
match ScopeId::ROOT.has_context() {
|
||||
let rt = Runtime::current();
|
||||
match rt.has_context(ScopeId::ROOT) {
|
||||
Some(context) => context,
|
||||
None => ScopeId::ROOT.provide_context(Default::default()),
|
||||
None => rt.provide_context(ScopeId::ROOT, Default::default()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,8 +56,7 @@ impl<T> Memo<T> {
|
||||
let _ = tx.unbounded_send(());
|
||||
}
|
||||
};
|
||||
let rc =
|
||||
ReactiveContext::new_with_callback(callback, current_scope_id().unwrap(), location);
|
||||
let rc = ReactiveContext::new_with_callback(callback, current_scope_id(), location);
|
||||
|
||||
// Create a new signal in that context, wiring up its dependencies and subscribers
|
||||
let mut recompute = move || rc.reset_and_run_in(&mut f);
|
||||
|
||||
@@ -9,9 +9,13 @@ pub fn copy_value_hoisted<T: 'static, S: generational_box::Storage<T> + 'static>
|
||||
caller: &'static std::panic::Location<'static>,
|
||||
) {
|
||||
let origin_scope = value.origin_scope;
|
||||
if let Ok(current_scope) = dioxus_core::current_scope_id() {
|
||||
let Some(rt) = dioxus_core::Runtime::try_current() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(current_scope) = rt.try_current_scope_id() {
|
||||
// If the current scope is a descendant of the origin scope or is the same scope, we don't need to warn
|
||||
if origin_scope == current_scope || current_scope.is_descendant_of(origin_scope) {
|
||||
if origin_scope == current_scope || rt.is_descendant_of(current_scope, origin_scope) {
|
||||
return;
|
||||
}
|
||||
let create_location = value.value.created_at().unwrap();
|
||||
|
||||
@@ -61,7 +61,7 @@ fn reading_subscribes() {
|
||||
.counter
|
||||
.borrow_mut()
|
||||
.children
|
||||
.entry(current_scope_id().unwrap())
|
||||
.entry(current_scope_id())
|
||||
.or_default() += 1;
|
||||
|
||||
rsx! {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use dioxus_core::provide_context;
|
||||
use dioxus_core::queue_effect;
|
||||
use dioxus_core::ScopeId;
|
||||
use dioxus_core::{provide_context, Runtime};
|
||||
use dioxus_document::{
|
||||
Document, Eval, EvalError, Evaluator, LinkProps, MetaProps, ScriptProps, StyleProps,
|
||||
};
|
||||
@@ -69,8 +69,7 @@ extern "C" {
|
||||
|
||||
fn init_document_with(document: impl FnOnce(), history: impl FnOnce()) {
|
||||
use dioxus_core::has_context;
|
||||
|
||||
ScopeId::ROOT.in_runtime(|| {
|
||||
Runtime::current().in_scope(ScopeId::ROOT, || {
|
||||
if has_context::<Rc<dyn Document>>().is_none() {
|
||||
document();
|
||||
}
|
||||
@@ -80,7 +79,7 @@ fn init_document_with(document: impl FnOnce(), history: impl FnOnce()) {
|
||||
})
|
||||
}
|
||||
|
||||
/// Provides the Document through [`ScopeId::provide_context`].
|
||||
/// Provides the Document through [`dioxus_core::provide_context`].
|
||||
pub fn init_document() {
|
||||
// If hydrate is enabled, we add the FullstackWebDocument with the initial hydration data
|
||||
#[cfg(not(feature = "hydrate"))]
|
||||
|
||||
@@ -149,7 +149,7 @@ impl WebsysDom {
|
||||
let server_data = HydrationContext::from_serialized(&data, debug_types, debug_locations);
|
||||
// If the server serialized an error into the suspense boundary, throw it on the client so that it bubbles up to the nearest error boundary
|
||||
if let Some(error) = server_data.error_entry().get().ok().flatten() {
|
||||
dom.in_runtime(|| id.throw_error(error));
|
||||
dom.in_runtime(|| dom.runtime().throw_error(id, error));
|
||||
}
|
||||
server_data.in_context(|| {
|
||||
// rerun the scope with the new data
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
pub use crate::cfg::Config;
|
||||
use crate::hydration::SuspenseMessage;
|
||||
use dioxus_core::VirtualDom;
|
||||
use dioxus_core::{ScopeId, VirtualDom};
|
||||
use dom::WebsysDom;
|
||||
use futures_util::{pin_mut, select, FutureExt, StreamExt};
|
||||
|
||||
@@ -53,7 +53,7 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! {
|
||||
|
||||
#[cfg(feature = "document")]
|
||||
if let Some(history) = web_config.history.clone() {
|
||||
virtual_dom.in_runtime(|| dioxus_core::ScopeId::ROOT.provide_context(history));
|
||||
virtual_dom.in_scope(ScopeId::ROOT, || dioxus_core::provide_context(history));
|
||||
}
|
||||
|
||||
#[cfg(feature = "document")]
|
||||
@@ -129,16 +129,14 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! {
|
||||
HydrationContext::from_serialized(&hydration_data, debug_types, debug_locations);
|
||||
// If the server serialized an error into the root suspense boundary, throw it into the root scope
|
||||
if let Some(error) = server_data.error_entry().get().ok().flatten() {
|
||||
virtual_dom.in_runtime(|| dioxus_core::ScopeId::APP.throw_error(error));
|
||||
virtual_dom.in_runtime(|| virtual_dom.runtime().throw_error(ScopeId::APP, error));
|
||||
}
|
||||
server_data.in_context(|| {
|
||||
virtual_dom.in_runtime(|| {
|
||||
virtual_dom.in_scope(ScopeId::ROOT, || {
|
||||
// Provide a hydration compatible create error boundary method
|
||||
dioxus_core::ScopeId::ROOT.in_runtime(|| {
|
||||
dioxus_core::provide_create_error_boundary(
|
||||
dioxus_fullstack_core::init_error_boundary,
|
||||
);
|
||||
});
|
||||
dioxus_core::provide_create_error_boundary(
|
||||
dioxus_fullstack_core::init_error_boundary,
|
||||
);
|
||||
#[cfg(feature = "document")]
|
||||
document::init_fullstack_document();
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user