Merge branch 'master' into events-2
This commit is contained in:
commit
219c068283
368 files changed
+1877
-13500
No files matched your search
@@ -68,7 +68,7 @@ unsafe impl<'a, P> AnyProps<'a> for VProps<'a, P> {
|
||||
Ok(None) => RenderReturn::default(),
|
||||
Err(err) => {
|
||||
let component_name = cx.name();
|
||||
log::error!("Error while rendering component `{component_name}`: {err:?}");
|
||||
tracing::error!("Error while rendering component `{component_name}`: {err:?}");
|
||||
RenderReturn::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +196,8 @@ impl<'b> VirtualDom {
|
||||
right.scope.set(Some(scope_id));
|
||||
|
||||
// copy out the box for both
|
||||
let old = self.scopes[scope_id.0].props.as_ref();
|
||||
let old_scope = &self.scopes[scope_id.0];
|
||||
let old = old_scope.props.as_ref();
|
||||
let new: Box<dyn AnyProps> = right.props.take().unwrap();
|
||||
let new: Box<dyn AnyProps> = unsafe { std::mem::transmute(new) };
|
||||
|
||||
@@ -204,6 +205,11 @@ impl<'b> VirtualDom {
|
||||
// The target scopestate still has the reference to the old props, so there's no need to update anything
|
||||
// This also implicitly drops the new props since they're not used
|
||||
if left.static_props && unsafe { old.as_ref().unwrap().memoize(new.as_ref()) } {
|
||||
tracing::trace!(
|
||||
"Memoized props for component {:#?} ({})",
|
||||
scope_id,
|
||||
old_scope.context().name
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,17 +49,16 @@ impl ErrorBoundary {
|
||||
/// A trait to allow results to be thrown upwards to the nearest Error Boundary
|
||||
///
|
||||
/// The canonical way of using this trait is to throw results from hooks, aborting rendering
|
||||
/// through question mark synax. The throw method returns an option that evalutes to None
|
||||
/// through question mark syntax. The throw method returns an option that evaluates to None
|
||||
/// if there is an error, injecting the error to the nearest error boundary.
|
||||
///
|
||||
/// If the value is `Ok`, then throw returns the value, not aborting the rendering preocess.
|
||||
/// If the value is `Ok`, then throw returns the value, not aborting the rendering process.
|
||||
///
|
||||
/// The call stack is saved for this component and provided to the error boundary
|
||||
///
|
||||
/// ```rust, ignore
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// fn app(cx: Scope, count: String) -> Element {
|
||||
/// #[component]
|
||||
/// fn App(cx: Scope, count: String) -> Element {
|
||||
/// let id: i32 = count.parse().throw(cx)?;
|
||||
///
|
||||
/// cx.render(rsx! {
|
||||
@@ -71,9 +70,9 @@ pub trait Throw<S = ()>: Sized {
|
||||
/// The value that will be returned in if the given value is `Ok`.
|
||||
type Out;
|
||||
|
||||
/// Returns an option that evalutes to None if there is an error, injecting the error to the nearest error boundary.
|
||||
/// Returns an option that evaluates to None if there is an error, injecting the error to the nearest error boundary.
|
||||
///
|
||||
/// If the value is `Ok`, then throw returns the value, not aborting the rendering preocess.
|
||||
/// If the value is `Ok`, then throw returns the value, not aborting the rendering process.
|
||||
///
|
||||
/// The call stack is saved for this component and provided to the error boundary
|
||||
///
|
||||
@@ -83,9 +82,8 @@ pub trait Throw<S = ()>: Sized {
|
||||
///
|
||||
///
|
||||
/// ```rust, ignore
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// fn app(cx: Scope, count: String) -> Element {
|
||||
/// #[component]
|
||||
/// fn App(cx: Scope, count: String) -> Element {
|
||||
/// let id: i32 = count.parse().throw(cx)?;
|
||||
///
|
||||
/// cx.render(rsx! {
|
||||
@@ -95,9 +93,9 @@ pub trait Throw<S = ()>: Sized {
|
||||
/// ```
|
||||
fn throw(self, cx: &ScopeState) -> Option<Self::Out>;
|
||||
|
||||
/// Returns an option that evalutes to None if there is an error, injecting the error to the nearest error boundary.
|
||||
/// Returns an option that evaluates to None if there is an error, injecting the error to the nearest error boundary.
|
||||
///
|
||||
/// If the value is `Ok`, then throw returns the value, not aborting the rendering preocess.
|
||||
/// If the value is `Ok`, then throw returns the value, not aborting the rendering process.
|
||||
///
|
||||
/// The call stack is saved for this component and provided to the error boundary
|
||||
///
|
||||
@@ -107,9 +105,8 @@ pub trait Throw<S = ()>: Sized {
|
||||
///
|
||||
///
|
||||
/// ```rust, ignore
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// fn app(cx: Scope, count: String) -> Element {
|
||||
/// #[component]
|
||||
/// fn App(cx: Scope, count: String) -> Element {
|
||||
/// let id: i32 = count.parse().throw(cx)?;
|
||||
///
|
||||
/// cx.render(rsx! {
|
||||
|
||||
@@ -139,7 +139,7 @@ impl<T: std::fmt::Debug> std::fmt::Debug for Event<T> {
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// rsx!{
|
||||
/// MyComponent { onclick: move |evt| log::info!("clicked") }
|
||||
/// MyComponent { onclick: move |evt| tracing::debug!("clicked") }
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Props)]
|
||||
|
||||
@@ -32,6 +32,7 @@ pub(crate) mod innerlude {
|
||||
pub use crate::nodes::RenderReturn;
|
||||
pub use crate::nodes::*;
|
||||
pub use crate::properties::*;
|
||||
pub use crate::runtime::{in_runtime, override_runtime, Runtime};
|
||||
pub use crate::scheduler::*;
|
||||
pub use crate::scope_context::*;
|
||||
pub use crate::scopes::*;
|
||||
@@ -86,11 +87,11 @@ pub use crate::innerlude::{
|
||||
pub mod prelude {
|
||||
pub use crate::innerlude::{
|
||||
consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, has_context,
|
||||
provide_context, provide_context_to_scope, provide_root_context, push_future,
|
||||
remove_future, schedule_update_any, spawn, spawn_forever, suspend, throw, AnyValue,
|
||||
Component, Element, Event, EventHandler, Fragment, IntoAttributeValue, LazyNodes,
|
||||
Properties, Scope, ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute,
|
||||
TemplateNode, Throw, VNode, VirtualDom,
|
||||
in_runtime, override_runtime, provide_context, provide_context_to_scope,
|
||||
provide_root_context, push_future, remove_future, schedule_update_any, spawn,
|
||||
spawn_forever, suspend, throw, AnyValue, Component, Element, Event, EventHandler, Fragment,
|
||||
IntoAttributeValue, LazyNodes, Properties, Runtime, Scope, ScopeId, ScopeState, Scoped,
|
||||
TaskId, Template, TemplateAttribute, TemplateNode, Throw, VNode, VirtualDom,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,51 @@ thread_local! {
|
||||
static RUNTIMES: RefCell<Vec<Rc<Runtime>>> = RefCell::new(vec![]);
|
||||
}
|
||||
|
||||
/// Run some code within a runtime
|
||||
pub fn in_runtime<R>(runtime: Rc<Runtime>, f: impl FnOnce() -> R) -> R {
|
||||
let _guard = RuntimeGuard::new(runtime);
|
||||
f()
|
||||
}
|
||||
|
||||
/// Override the current runtime. This must be used to override the current runtime when importing components from a dynamic library that has it's own runtime.
|
||||
///
|
||||
/// ```rust
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let virtual_dom = VirtualDom::new(app);
|
||||
/// }
|
||||
///
|
||||
/// fn app(cx: Scope) -> Element {
|
||||
/// render!{ Component { runtime: Runtime::current().unwrap() } }
|
||||
/// }
|
||||
///
|
||||
/// // In a dynamic library
|
||||
/// #[derive(Props)]
|
||||
/// struct ComponentProps {
|
||||
/// runtime: std::rc::Rc<Runtime>,
|
||||
/// }
|
||||
///
|
||||
/// impl PartialEq for ComponentProps {
|
||||
/// fn eq(&self, _other: &Self) -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn Component(cx: Scope<ComponentProps>) -> Element {
|
||||
/// cx.use_hook(|| override_runtime(cx.props.runtime.clone()));
|
||||
///
|
||||
/// render! { div {} }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn override_runtime(runtime: Rc<Runtime>) {
|
||||
RUNTIMES.with(|stack| {
|
||||
let mut stack = stack.borrow_mut();
|
||||
stack.pop();
|
||||
stack.push(runtime);
|
||||
});
|
||||
}
|
||||
|
||||
/// Pushes a new scope onto the stack
|
||||
pub(crate) fn push_runtime(runtime: Rc<Runtime>) {
|
||||
RUNTIMES.with(|stack| stack.borrow_mut().push(runtime));
|
||||
@@ -41,7 +86,8 @@ where
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub(crate) struct Runtime {
|
||||
/// A global runtime that is shared across all scopes that provides the async runtime and context API
|
||||
pub struct Runtime {
|
||||
pub(crate) scope_contexts: RefCell<Vec<Option<ScopeContext>>>,
|
||||
pub(crate) scheduler: Rc<Scheduler>,
|
||||
|
||||
@@ -63,6 +109,11 @@ impl Runtime {
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the current runtime
|
||||
pub fn current() -> Option<Rc<Self>> {
|
||||
RUNTIMES.with(|stack| stack.borrow().last().cloned())
|
||||
}
|
||||
|
||||
/// Create a scope context. This slab is synchronized with the scope slab.
|
||||
pub(crate) fn create_context_at(&self, id: ScopeId, context: ScopeContext) {
|
||||
let mut contexts = self.scope_contexts.borrow_mut();
|
||||
@@ -77,14 +128,14 @@ impl Runtime {
|
||||
}
|
||||
|
||||
/// Get the current scope id
|
||||
pub fn current_scope_id(&self) -> Option<ScopeId> {
|
||||
pub(crate) fn current_scope_id(&self) -> Option<ScopeId> {
|
||||
self.scope_stack.borrow().last().copied()
|
||||
}
|
||||
|
||||
/// Get the context for any scope given its ID
|
||||
///
|
||||
/// This is useful for inserting or removing contexts from a scope, or rendering out its root node
|
||||
pub fn get_context(&self, id: ScopeId) -> Option<Ref<'_, ScopeContext>> {
|
||||
pub(crate) fn get_context(&self, id: ScopeId) -> Option<Ref<'_, ScopeContext>> {
|
||||
Ref::filter_map(self.scope_contexts.borrow(), |contexts| {
|
||||
contexts.get(id.0).and_then(|f| f.as_ref())
|
||||
})
|
||||
|
||||
@@ -64,6 +64,7 @@ impl VirtualDom {
|
||||
let props: &dyn AnyProps = scope.props.as_ref().unwrap().as_ref();
|
||||
let props: &dyn AnyProps = std::mem::transmute(props);
|
||||
|
||||
let _span = tracing::trace_span!("render", scope = %scope.context().name);
|
||||
props.render(scope).extend_lifetime()
|
||||
};
|
||||
|
||||
|
||||
@@ -107,20 +107,30 @@ impl ScopeContext {
|
||||
///
|
||||
/// 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.name
|
||||
);
|
||||
if let Some(this_ctx) = self.has_context() {
|
||||
return Some(this_ctx);
|
||||
}
|
||||
|
||||
let mut search_parent = self.parent_id;
|
||||
with_runtime(|runtime| {
|
||||
match with_runtime(|runtime: &crate::runtime::Runtime| {
|
||||
while let Some(parent_id) = search_parent {
|
||||
let parent = runtime.get_context(parent_id).unwrap();
|
||||
if let Some(shared) = parent
|
||||
.shared_contexts
|
||||
.borrow()
|
||||
.iter()
|
||||
.find_map(|any| any.downcast_ref::<T>())
|
||||
{
|
||||
tracing::trace!(
|
||||
"looking for context {} ({:?}) in {}",
|
||||
std::any::type_name::<T>(),
|
||||
std::any::TypeId::of::<T>(),
|
||||
parent.name
|
||||
);
|
||||
if let Some(shared) = parent.shared_contexts.borrow().iter().find_map(|any| {
|
||||
tracing::trace!("found context {:?}", any.type_id());
|
||||
any.downcast_ref::<T>()
|
||||
}) {
|
||||
return Some(shared.clone());
|
||||
}
|
||||
search_parent = parent.parent_id;
|
||||
@@ -128,6 +138,17 @@ impl ScopeContext {
|
||||
None
|
||||
})
|
||||
.flatten()
|
||||
{
|
||||
Some(ctx) => Some(ctx),
|
||||
None => {
|
||||
tracing::trace!(
|
||||
"context {} ({:?}) not found",
|
||||
std::any::type_name::<T>(),
|
||||
std::any::TypeId::of::<T>()
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Expose state to children further down the [`crate::VirtualDom`] Tree. Requires `Clone` on the context to allow getting values down the tree.
|
||||
@@ -152,6 +173,12 @@ impl ScopeContext {
|
||||
/// }
|
||||
/// ```
|
||||
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.name
|
||||
);
|
||||
let mut contexts = self.shared_contexts.borrow_mut();
|
||||
|
||||
// If the context exists, swap it out for the new value
|
||||
|
||||
@@ -52,6 +52,7 @@ use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc};
|
||||
///
|
||||
/// static ROUTES: &str = "";
|
||||
///
|
||||
/// #[component]
|
||||
/// fn App(cx: Scope<AppProps>) -> Element {
|
||||
/// cx.render(rsx!(
|
||||
/// NavBar { routes: ROUTES }
|
||||
@@ -60,18 +61,19 @@ use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc};
|
||||
/// ))
|
||||
/// }
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn NavBar(cx: Scope, routes: &'static str) -> Element {
|
||||
/// cx.render(rsx! {
|
||||
/// div { "Routes: {routes}" }
|
||||
/// })
|
||||
/// }
|
||||
///
|
||||
/// #[component]
|
||||
/// fn Footer(cx: Scope) -> Element {
|
||||
/// cx.render(rsx! { div { "Footer" } })
|
||||
/// }
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// #[component]
|
||||
/// fn Title<'a>(cx: Scope<'a>, children: Element<'a>) -> Element {
|
||||
/// cx.render(rsx! {
|
||||
/// div { id: "title", children }
|
||||
@@ -122,13 +124,14 @@ use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc};
|
||||
///
|
||||
/// Putting everything together, you can build an event loop around Dioxus by using the methods outlined above.
|
||||
/// ```rust, ignore
|
||||
/// fn app(cx: Scope) -> Element {
|
||||
/// #[component]
|
||||
/// fn App(cx: Scope) -> Element {
|
||||
/// cx.render(rsx! {
|
||||
/// div { "Hello World" }
|
||||
/// })
|
||||
/// }
|
||||
///
|
||||
/// let dom = VirtualDom::new(app);
|
||||
/// let dom = VirtualDom::new(App);
|
||||
///
|
||||
/// real_dom.apply(dom.rebuild());
|
||||
///
|
||||
@@ -306,6 +309,7 @@ impl VirtualDom {
|
||||
pub fn mark_dirty(&mut self, id: ScopeId) {
|
||||
if let Some(scope) = self.get_scope(id) {
|
||||
let height = scope.height();
|
||||
tracing::trace!("Marking scope {:?} ({}) as dirty", id, scope.context().name);
|
||||
self.dirty_scopes.insert(DirtyScope { height, id });
|
||||
}
|
||||
}
|
||||
@@ -558,7 +562,7 @@ impl VirtualDom {
|
||||
}
|
||||
// If an error occurs, we should try to render the default error component and context where the error occured
|
||||
RenderReturn::Aborted(placeholder) => {
|
||||
log::info!("Ran into suspended or aborted scope during rebuild");
|
||||
tracing::debug!("Ran into suspended or aborted scope during rebuild");
|
||||
let id = self.next_null();
|
||||
placeholder.id.set(Some(id));
|
||||
self.mutations.push(Mutation::CreatePlaceholder { id });
|
||||
@@ -658,6 +662,11 @@ impl VirtualDom {
|
||||
fn finalize(&mut self) -> Mutations {
|
||||
std::mem::take(&mut self.mutations)
|
||||
}
|
||||
|
||||
/// Get the current runtime
|
||||
pub fn runtime(&self) -> Rc<Runtime> {
|
||||
self.runtime.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VirtualDom {
|
||||
|
||||
Reference in new issue
Block a user