hello world example working

This commit is contained in:
Evan Almloff committed 2024-01-05 18:01:05 -06:00
1 parent 3865f44081
commit 4013341b6c
22 files changed
+159 -176

No files matched your search

-1
View File
@@ -17,7 +17,6 @@ proc-macro2 = { version = "1.0" }
quote = "1.0" quote = "1.0"
syn = { version = "2.0", features = ["full", "extra-traits"] } syn = { version = "2.0", features = ["full", "extra-traits"] }
dioxus-rsx = { workspace = true } dioxus-rsx = { workspace = true }
dioxus-core = { workspace = true }
constcat = "0.3.0" constcat = "0.3.0"
prettyplease = "0.2.15" prettyplease = "0.2.15"
+1 -34
View File
@@ -147,9 +147,7 @@ pub mod utils;
pub use utils::DeserializerArgs; pub use utils::DeserializerArgs;
pub use utils::DeserializerOutput; pub use utils::DeserializerOutput;
pub use utils::TypeHelper;
use dioxus_core::{Element, Scope};
use syn::parse::{Parse, ParseStream}; use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::*; use syn::*;
@@ -167,12 +165,6 @@ pub struct ComponentBody {
/// Keep this in mind when creating deserializers, because you often might want to ignore it. /// Keep this in mind when creating deserializers, because you often might want to ignore it.
/// That might be annoying, but it would be bad design for this kind of struct to not be parsable from itself. /// That might be annoying, but it would be bad design for this kind of struct to not be parsable from itself.
pub item_fn: ItemFn, pub item_fn: ItemFn,
/// Parsing tries to ensure that this argument will be a [`Scope`].
/// **However, macros have limitations that prevent this from always working,
/// so don't take this for granted!**
pub cx_arg: FnArg,
/// The pattern (name) and type of the context argument.
pub cx_pat_type: PatType,
/// If the function has any arguments other than the context. /// If the function has any arguments other than the context.
pub has_extra_args: bool, pub has_extra_args: bool,
} }
@@ -192,31 +184,8 @@ impl ComponentBody {
impl Parse for ComponentBody { impl Parse for ComponentBody {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let item_fn: ItemFn = input.parse()?; let item_fn: ItemFn = input.parse()?;
let scope_type_path = Scope::get_path_string();
let (cx_arg, cx_pat_type) = if let Some(first_arg) = item_fn.sig.inputs.first() { let element_type_path = "::dioxus::core::Element";
let incorrect_first_arg_err = Err(Error::new(
first_arg.span(),
format!("First argument must be a <{}>", scope_type_path),
));
match first_arg.to_owned() {
FnArg::Receiver(_) => {
return incorrect_first_arg_err;
}
FnArg::Typed(f) => (first_arg.to_owned(), f),
}
} else {
return Err(Error::new(
item_fn.sig.ident.span(), // Or maybe just item_f.sig.span()?
format!(
"Must have at least one argument that's a <{}>",
scope_type_path
),
));
};
let element_type_path = Element::get_path_string();
if item_fn.sig.output == ReturnType::Default { if item_fn.sig.output == ReturnType::Default {
return Err(Error::new( return Err(Error::new(
@@ -229,8 +198,6 @@ impl Parse for ComponentBody {
Ok(Self { Ok(Self {
item_fn, item_fn,
cx_arg,
cx_pat_type,
has_extra_args, has_extra_args,
}) })
} }
@@ -1,5 +1,4 @@
use crate::component_body::ComponentBody; use crate::component_body::ComponentBody;
use dioxus_core::{Element, Scope};
use quote::ToTokens; use quote::ToTokens;
use syn::{parse_quote, Path}; use syn::{parse_quote, Path};
@@ -36,22 +35,3 @@ where
#[allow(unused_qualifications)] #[allow(unused_qualifications)]
fn to_output(&self, component_body: &ComponentBody) -> syn::Result<TOutput>; fn to_output(&self, component_body: &ComponentBody) -> syn::Result<TOutput>;
} }
pub trait TypeHelper {
fn get_path() -> Path;
fn get_path_string() -> String {
Self::get_path().to_token_stream().to_string()
}
}
impl<'a> TypeHelper for Scope<'a> {
fn get_path() -> Path {
parse_quote!(::dioxus::core::Scope)
}
}
impl<'a> TypeHelper for Element<'a> {
fn get_path() -> Path {
parse_quote!(::dioxus::core::Element)
}
}
@@ -16,7 +16,7 @@ To ignore this check, pass the \"",
const INNER_FN_NAME: &str = "__dx_inner_comp"; const INNER_FN_NAME: &str = "__dx_inner_comp";
fn get_out_comp_fn(orig_comp_fn: &ItemFn, cx_pat: &Pat) -> ItemFn { fn get_out_comp_fn(orig_comp_fn: &ItemFn) -> ItemFn {
let inner_comp_ident = Ident::new(INNER_FN_NAME, orig_comp_fn.sig.ident.span()); let inner_comp_ident = Ident::new(INNER_FN_NAME, orig_comp_fn.sig.ident.span());
let inner_comp_fn = ItemFn { let inner_comp_fn = ItemFn {
@@ -34,7 +34,7 @@ fn get_out_comp_fn(orig_comp_fn: &ItemFn, cx_pat: &Pat) -> ItemFn {
#[allow(clippy::inline_always)] #[allow(clippy::inline_always)]
#[inline(always)] #[inline(always)]
#inner_comp_fn #inner_comp_fn
#inner_comp_ident (#cx_pat) #inner_comp_ident(__props)
} }
}, },
..orig_comp_fn.clone() ..orig_comp_fn.clone()
@@ -88,14 +88,9 @@ impl DeserializerArgs<ComponentDeserializerOutput> for ComponentDeserializerArgs
impl ComponentDeserializerArgs { impl ComponentDeserializerArgs {
fn deserialize_no_props(component_body: &ComponentBody) -> ComponentDeserializerOutput { fn deserialize_no_props(component_body: &ComponentBody) -> ComponentDeserializerOutput {
let ComponentBody { let ComponentBody { item_fn, .. } = component_body;
item_fn,
cx_pat_type,
..
} = component_body;
let cx_pat = &cx_pat_type.pat;
let comp_fn = get_out_comp_fn(item_fn, cx_pat); let comp_fn = get_out_comp_fn(item_fn);
ComponentDeserializerOutput { ComponentDeserializerOutput {
comp_fn, comp_fn,
@@ -106,12 +101,7 @@ impl ComponentDeserializerArgs {
fn deserialize_with_props( fn deserialize_with_props(
component_body: &ComponentBody, component_body: &ComponentBody,
) -> Result<ComponentDeserializerOutput> { ) -> Result<ComponentDeserializerOutput> {
let ComponentBody { let ComponentBody { item_fn, .. } = component_body;
item_fn,
cx_pat_type,
..
} = component_body;
let cx_pat = &cx_pat_type.pat;
let comp_parsed = match parse2::<ComponentBody>(quote!(#item_fn)) { let comp_parsed = match parse2::<ComponentBody>(quote!(#item_fn)) {
Ok(comp_body) => comp_body, Ok(comp_body) => comp_body,
@@ -130,7 +120,7 @@ impl ComponentDeserializerArgs {
let props_struct = inlined_props_output.props_struct; let props_struct = inlined_props_output.props_struct;
let props_fn = inlined_props_output.comp_fn; let props_fn = inlined_props_output.comp_fn;
let comp_fn = get_out_comp_fn(&props_fn, cx_pat); let comp_fn = get_out_comp_fn(&props_fn);
Ok(ComponentDeserializerOutput { Ok(ComponentDeserializerOutput {
comp_fn, comp_fn,
@@ -225,11 +225,7 @@ fn get_props_docs(fn_ident: &Ident, inputs: Vec<&FnArg>) -> Vec<Attribute> {
} }
fn get_function(component_body: &ComponentBody) -> ItemFn { fn get_function(component_body: &ComponentBody) -> ItemFn {
let ComponentBody { let ComponentBody { item_fn, .. } = component_body;
item_fn,
cx_pat_type,
..
} = component_body;
let ItemFn { let ItemFn {
attrs: fn_attrs, attrs: fn_attrs,
vis, vis,
@@ -246,7 +242,6 @@ fn get_function(component_body: &ComponentBody) -> ItemFn {
} = sig; } = sig;
let Generics { where_clause, .. } = generics; let Generics { where_clause, .. } = generics;
let cx_pat = &cx_pat_type.pat;
let struct_ident = Ident::new(&format!("{fn_ident}Props"), fn_ident.span()); let struct_ident = Ident::new(&format!("{fn_ident}Props"), fn_ident.span());
// Skip first arg since that's the context // Skip first arg since that's the context
@@ -298,10 +293,10 @@ fn get_function(component_body: &ComponentBody) -> ItemFn {
parse_quote! { parse_quote! {
#(#fn_attrs)* #(#fn_attrs)*
#(#props_docs)* #(#props_docs)*
#asyncness #vis fn #fn_ident #fn_generics (#cx_pat: Scope<#scope_lifetime #struct_ident #generics_no_bounds>) #fn_output #asyncness #vis fn #fn_ident #fn_generics (__props: #struct_ident #generics_no_bounds) #fn_output
#where_clause #where_clause
{ {
let #struct_ident { #(#struct_field_names),* } = &#cx_pat.props; let #struct_ident { #(#struct_field_names),* } = &__props;
#fn_block #fn_block
} }
} }
+1 -1
View File
@@ -155,7 +155,7 @@ impl VirtualDom {
} }
Element { .. } => { Element { .. } => {
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
let id = self.write_element_root(node, idx, &mut attrs, &mut nodes, &[]); let id = self.write_element_root(node, idx, &mut attrs, &mut nodes, &[], to);
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
let id = self.write_element_root( let id = self.write_element_root(
node, node,
+15 -10
View File
@@ -37,11 +37,22 @@ impl Debug for ErrorBoundaryInner {
} }
} }
/// A trait for any type that can be downcast to a concrete type and implements Debug. This is automatically implemented for all types that implement Any + Debug.
pub trait AnyDebug: Any + Debug {
fn as_any(&self) -> &dyn Any;
}
impl<T: Any + Debug> AnyDebug for T {
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Debug)] #[derive(Debug)]
/// An instance of an error captured by a descendant component. /// An instance of an error captured by a descendant component.
pub struct CapturedError { pub struct CapturedError {
/// The error captured by the error boundary /// The error captured by the error boundary
pub error: Box<dyn Debug + 'static>, pub error: Box<dyn AnyDebug + 'static>,
/// The backtrace of the error /// The backtrace of the error
pub backtrace: Backtrace, pub backtrace: Backtrace,
@@ -65,8 +76,7 @@ impl CapturedError {
/// Downcast the error type into a concrete error type /// Downcast the error type into a concrete error type
pub fn downcast<T: 'static>(&self) -> Option<&T> { pub fn downcast<T: 'static>(&self) -> Option<&T> {
if TypeId::of::<T>() == self.error.type_id() { if TypeId::of::<T>() == self.error.type_id() {
let raw = self.error.as_ref() as *const _ as *const T; self.error.as_any().downcast_ref::<T>()
Some(unsafe { &*raw })
} else { } else {
None None
} }
@@ -100,14 +110,9 @@ impl ErrorBoundary {
} }
/// Push an error into this Error Boundary /// Push an error into this Error Boundary
pub fn insert_error( pub fn insert_error(&self, scope: ScopeId, error: impl Debug + 'static, backtrace: Backtrace) {
&self,
scope: ScopeId,
error: Box<dyn Debug + 'static>,
backtrace: Backtrace,
) {
self.inner.error.replace(Some(CapturedError { self.inner.error.replace(Some(CapturedError {
error, error: Box::new(error),
scope, scope,
backtrace, backtrace,
})); }));
+20
View File
@@ -1,3 +1,5 @@
use std::sync::Arc;
use futures_util::Future; use futures_util::Future;
use crate::{ use crate::{
@@ -113,3 +115,21 @@ pub fn parent_scope() -> Option<ScopeId> {
pub fn needs_update() { pub fn needs_update() {
with_current_scope(|cx| cx.needs_update()); with_current_scope(|cx| cx.needs_update());
} }
/// Schedule an update for the current component
///
/// Note: Unlike [`needs_update`], the function returned by this method will work outside of the dioxus runtime.
///
/// You should prefer [`schedule_update_any`] if you need to update multiple components.
pub fn schedule_update() -> Arc<dyn Fn() + Send + Sync> {
with_current_scope(|cx| cx.schedule_update()).expect("to be in a dioxus runtime")
}
/// Schedule an update for any component given its [`ScopeId`].
///
/// A component's [`ScopeId`] can be obtained from the [`current_scope_id`] method.
///
/// Note: Unlike [`needs_update`], the function returned by this method will work outside of the dioxus runtime.
pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
with_current_scope(|cx| cx.schedule_update_any()).expect("to be in a dioxus runtime")
}
+9 -8
View File
@@ -74,10 +74,11 @@ pub(crate) mod innerlude {
} }
pub use crate::innerlude::{ pub use crate::innerlude::{
fc_to_builder, generation, once, vdom_is_rendering, AnyValue, Attribute, AttributeValue, fc_to_builder, generation, once, schedule_update, schedule_update_any, vdom_is_rendering,
CapturedError, Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, AnyValue, Attribute, AttributeValue, CapturedError, Component, DynamicNode, Element, ElementId,
Mutation, MutationsVec, Properties, RenderReturn, ScopeId, Task, Template, TemplateAttribute, Event, Fragment, IntoDynNode, Mutation, MutationsVec, Properties, RenderReturn, ScopeId, Task,
TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom, WriteMutations, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom,
WriteMutations,
}; };
/// The purpose of this module is to alleviate imports of many common types /// The purpose of this module is to alleviate imports of many common types
@@ -87,9 +88,9 @@ pub mod prelude {
pub use crate::innerlude::{ pub use crate::innerlude::{
consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, generation, consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, generation,
has_context, needs_update, once, parent_scope, provide_context, provide_root_context, has_context, needs_update, once, parent_scope, provide_context, provide_root_context,
push_future, remove_future, spawn, spawn_forever, suspend, use_error_boundary, AnyValue, push_future, remove_future, schedule_update, schedule_update_any, spawn, spawn_forever,
Component, Element, ErrorBoundary, Event, EventHandler, Fragment, IntoAttributeValue, suspend, use_error_boundary, AnyValue, Component, Element, ErrorBoundary, Event,
IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, Task, Template, TemplateAttribute, EventHandler, Fragment, IntoAttributeValue, IntoDynNode, Properties, Runtime, RuntimeGuard,
TemplateNode, Throw, VNode, VirtualDom, ScopeId, Task, Template, TemplateAttribute, TemplateNode, Throw, VNode, VirtualDom,
}; };
} }
+28
View File
@@ -9,6 +9,7 @@ use std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
future::Future, future::Future,
rc::Rc, rc::Rc,
sync::Arc,
}; };
/// A component's state separate from its props. /// A component's state separate from its props.
@@ -69,6 +70,26 @@ impl ScopeContext {
.expect("Scheduler to exist if scope exists"); .expect("Scheduler to exist if scope exists");
} }
/// Create a subscription that schedules a future render for the reference component
///
/// ## Notice: you should prefer using [`Self::schedule_update_any`] and [`Self::scope_id`]
pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
let (chan, id) = (self.tasks.sender.clone(), self.id);
Arc::new(move || drop(chan.unbounded_send(SchedulerMsg::Immediate(id))))
}
/// Schedule an update for any component given its [`ScopeId`].
///
/// A component's [`ScopeId`] can be obtained from `use_hook` or the [`ScopeState::scope_id`] method.
///
/// This method should be used when you want to schedule an update for a component
pub fn schedule_update_any(&self) -> Arc<dyn Fn(ScopeId) + Send + Sync> {
let chan = self.tasks.sender.clone();
Arc::new(move |id| {
chan.unbounded_send(SchedulerMsg::Immediate(id)).unwrap();
})
}
/// Return any context of type T if it exists on this scope /// Return any context of type T if it exists on this scope
pub fn has_context<T: 'static + Clone>(&self) -> Option<T> { pub fn has_context<T: 'static + Clone>(&self) -> Option<T> {
self.shared_contexts self.shared_contexts
@@ -355,4 +376,11 @@ impl ScopeId {
pub fn needs_update(self) { pub fn needs_update(self) {
with_scope(self, |cx| cx.needs_update()); with_scope(self, |cx| cx.needs_update());
} }
/// 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 [`dioxus_core::schedule_update_any`] and [`Self::scope_id`]
pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
with_scope(*self, |cx| cx.schedule_update()).expect("to be in a dioxus runtime")
}
} }
+3 -1
View File
@@ -549,7 +549,9 @@ impl VirtualDom {
pub fn rebuild(&mut self, to: &mut impl WriteMutations) { pub fn rebuild(&mut self, to: &mut impl WriteMutations) {
self.flush_templates(to); self.flush_templates(to);
let _runtime = RuntimeGuard::new(self.runtime.clone()); let _runtime = RuntimeGuard::new(self.runtime.clone());
match self.run_scope(ScopeId::ROOT) { let new_nodes = self.run_scope(ScopeId::ROOT);
self.scopes[ScopeId::ROOT.0].last_rendered_node = Some(new_nodes.clone());
match new_nodes {
// Rebuilding implies we append the created elements to the root // Rebuilding implies we append the created elements to the root
RenderReturn::Ready(node) => { RenderReturn::Ready(node) => {
let m = self.create_scope(ScopeId::ROOT, &node, to); let m = self.create_scope(ScopeId::ROOT, &node, to);
+7 -6
View File
@@ -2,6 +2,7 @@
use async_trait::async_trait; use async_trait::async_trait;
use dioxus_core::prelude::consume_context; use dioxus_core::prelude::consume_context;
use dioxus_core::prelude::provide_context; use dioxus_core::prelude::provide_context;
use dioxus_core::ScopeId;
use dioxus_html::prelude::{EvalError, EvalProvider, Evaluator}; use dioxus_html::prelude::{EvalError, EvalProvider, Evaluator};
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
@@ -9,13 +10,13 @@ use crate::{query::Query, DesktopContext};
/// Provides the DesktopEvalProvider through [`cx.provide_context`]. /// Provides the DesktopEvalProvider through [`cx.provide_context`].
pub fn init_eval() { pub fn init_eval() {
let desktop_ctx = consume_context::<DesktopContext>().unwrap(); let desktop_ctx = ScopeId::ROOT.consume_context::<DesktopContext>().unwrap();
let provider: Rc<dyn EvalProvider> = Rc::new(DesktopEvalProvider { desktop_ctx }); let provider: Rc<dyn EvalProvider> = Rc::new(DesktopEvalProvider { desktop_ctx });
provide_context(provider); ScopeId::ROOT.provide_context(provider);
} }
/// Reprents the desktop-target's provider of evaluators. /// Represents the desktop-target's provider of evaluators.
pub struct DesktopEvalProvider { pub(crate) struct DesktopEvalProvider {
desktop_ctx: DesktopContext, desktop_ctx: DesktopContext,
} }
@@ -25,8 +26,8 @@ impl EvalProvider for DesktopEvalProvider {
} }
} }
/// Reprents a desktop-target's JavaScript evaluator. /// Represents a desktop-target's JavaScript evaluator.
pub struct DesktopEvaluator { pub(crate) struct DesktopEvaluator {
query: Rc<RefCell<Query<serde_json::Value>>>, query: Rc<RefCell<Query<serde_json::Value>>>,
} }
+27 -27
View File
@@ -55,42 +55,42 @@ macro_rules! to_owned {
}; };
} }
pub mod computed; // pub mod computed;
mod use_on_destroy; // mod use_on_destroy;
pub use use_on_destroy::*; // pub use use_on_destroy::*;
mod use_const; // mod use_const;
pub use use_const::*; // pub use use_const::*;
mod use_context; // mod use_context;
pub use use_context::*; // pub use use_context::*;
mod use_state; // mod use_state;
pub use use_state::{use_state, UseState}; // pub use use_state::{use_state, UseState};
mod use_ref; // mod use_ref;
pub use use_ref::*; // pub use use_ref::*;
mod use_shared_state; // mod use_shared_state;
pub use use_shared_state::*; // pub use use_shared_state::*;
mod use_coroutine; // mod use_coroutine;
pub use use_coroutine::*; // pub use use_coroutine::*;
mod use_future; // mod use_future;
pub use use_future::*; // pub use use_future::*;
mod use_effect; // mod use_effect;
pub use use_effect::*; // pub use use_effect::*;
mod use_callback; // mod use_callback;
pub use use_callback::*; // pub use use_callback::*;
mod use_memo; // mod use_memo;
pub use use_memo::*; // pub use use_memo::*;
mod use_on_create; // mod use_on_create;
pub use use_on_create::*; // pub use use_on_create::*;
mod use_root_context; // mod use_root_context;
pub use use_root_context::*; // pub use use_root_context::*;
+2 -2
View File
@@ -68,7 +68,7 @@ impl ToTokens for ElementAttrNamed {
let value = &self.attr.value; let value = &self.attr.value;
let value = quote! { #value }; let value = quote! { #value };
quote! { quote! {
__cx.attr( ::dioxus::core::Attribute::new(
#attribute, #attribute,
#value, #value,
#ns, #ns,
@@ -79,7 +79,7 @@ impl ToTokens for ElementAttrNamed {
ElementAttrValue::EventTokens(tokens) => match &self.attr.name { ElementAttrValue::EventTokens(tokens) => match &self.attr.name {
ElementAttrName::BuiltIn(name) => { ElementAttrName::BuiltIn(name) => {
quote! { quote! {
dioxus_elements::events::#name(__cx, #tokens) dioxus_elements::events::#name(#tokens)
} }
} }
ElementAttrName::Custom(_) => todo!(), ElementAttrName::Custom(_) => todo!(),
+4 -4
View File
@@ -189,11 +189,11 @@ impl ToTokens for Component {
}; };
tokens.append_all(quote! { tokens.append_all(quote! {
__cx.component( ::dioxus::core::DynamicNode::Component(::dioxus::core::VComponent::new(
#gen_name, #gen_name,
#builder, #builder,
#fn_name #fn_name
) ))
}) })
} }
} }
@@ -217,10 +217,10 @@ impl ToTokens for ContentField {
match self { match self {
ContentField::ManExpr(e) => e.to_tokens(tokens), ContentField::ManExpr(e) => e.to_tokens(tokens),
ContentField::Formatted(s) => tokens.append_all(quote! { ContentField::Formatted(s) => tokens.append_all(quote! {
__cx.raw_text(#s) #s.to_string()
}), }),
ContentField::OnHandlerRaw(e) => tokens.append_all(quote! { ContentField::OnHandlerRaw(e) => tokens.append_all(quote! {
__cx.event_handler(#e) ::dioxus::core::Attribute::EventHandler(EventHandler::new(#e))
}), }),
} }
} }
+4 -4
View File
@@ -184,11 +184,11 @@ impl ToTokens for Element {
.filter(|f| !matches!(f.attr.value, ElementAttrValue::EventTokens { .. })); .filter(|f| !matches!(f.attr.value, ElementAttrValue::EventTokens { .. }));
tokens.append_all(quote! { tokens.append_all(quote! {
__cx.element( ::dioxus::core::Element::new(
#name, #name,
__cx.bump().alloc([ #(#listeners),* ]), vec![ #(#listeners),* ],
__cx.bump().alloc([ #(#attr),* ]), vec![ #(#attr),* ],
__cx.bump().alloc([ #(#children),* ]), vec![ #(#children),* ],
#key, #key,
) )
}); });
+7 -8
View File
@@ -69,7 +69,7 @@ impl CallBody {
&self, &self,
template: Option<CallBody>, template: Option<CallBody>,
location: &'static str, location: &'static str,
) -> Option<Template<'static>> { ) -> Option<Template> {
let mut renderer: TemplateRenderer = TemplateRenderer { let mut renderer: TemplateRenderer = TemplateRenderer {
roots: &self.roots, roots: &self.roots,
location: None, location: None,
@@ -138,7 +138,6 @@ impl ToTokens for RenderCallBody {
out_tokens.append_all(quote! { out_tokens.append_all(quote! {
Some({ Some({
let __cx = cx;
#body #body
}) })
}) })
@@ -156,7 +155,7 @@ impl<'a> TemplateRenderer<'a> {
&mut self, &mut self,
previous_call: Option<CallBody>, previous_call: Option<CallBody>,
location: &'static str, location: &'static str,
) -> Option<Template<'static>> { ) -> Option<Template> {
let mut mapping = previous_call.map(|call| DynamicMapping::from(call.roots)); let mut mapping = previous_call.map(|call| DynamicMapping::from(call.roots));
let mut context = DynamicContext::default(); let mut context = DynamicContext::default();
@@ -202,7 +201,7 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
}; };
let key_tokens = match key { let key_tokens = match key {
Some(tok) => quote! { Some( __cx.raw_text(#tok) ) }, Some(tok) => quote! { Some( #tok.to_string() ) },
None => quote! { None }, None => quote! { None },
}; };
@@ -257,9 +256,9 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
::dioxus::core::VNode::new( ::dioxus::core::VNode::new(
#key_tokens, #key_tokens,
TEMPLATE, TEMPLATE,
dioxus::core::exports::bumpalo::collections::Vec::with_capacity_in(#root_count, __cx.bump()), Vec::with_capacity(#root_count),
__cx.bump().alloc([ #( #node_printer ),* ]), vec![ #( #node_printer ),* ],
__cx.bump().alloc([ #( #dyn_attr_printer ),* ]), vec![ #( #dyn_attr_printer ),* ],
) )
}); });
} }
@@ -360,7 +359,7 @@ impl<'a> DynamicContext<'a> {
&mut self, &mut self,
root: &'a BodyNode, root: &'a BodyNode,
mapping: &mut Option<DynamicMapping>, mapping: &mut Option<DynamicMapping>,
) -> Option<TemplateNode<'static>> { ) -> Option<TemplateNode> {
match root { match root {
BodyNode::Element(el) => { BodyNode::Element(el) => {
let element_name_rust = el.name.to_string(); let element_name_rust = el.name.to_string();
+5 -5
View File
@@ -122,11 +122,11 @@ impl ToTokens for BodyNode {
BodyNode::Element(el) => el.to_tokens(tokens), BodyNode::Element(el) => el.to_tokens(tokens),
BodyNode::Component(comp) => comp.to_tokens(tokens), BodyNode::Component(comp) => comp.to_tokens(tokens),
BodyNode::Text(txt) => tokens.append_all(quote! { BodyNode::Text(txt) => tokens.append_all(quote! {
__cx.text_node(#txt) ::dioxus::core::DynamicNode::Text(::dioxus::core::VText::new(#txt.to_string()))
}), }),
BodyNode::RawExpr(exp) => tokens.append_all(quote! { BodyNode::RawExpr(exp) => tokens.append_all(quote! {
{ {
let ___nodes = (#exp).into_dyn_node(__cx); let ___nodes = (#exp).into_dyn_node();
___nodes ___nodes
} }
}), }),
@@ -145,7 +145,7 @@ impl ToTokens for BodyNode {
// And then we can return them into the dyn loop // And then we can return them into the dyn loop
tokens.append_all(quote! { tokens.append_all(quote! {
{ {
let ___nodes =(#expr).into_iter().map(|#pat| { #renderer }).into_dyn_node(__cx); let ___nodes = (#expr).into_iter().map(|#pat| { #renderer }).into_dyn_node();
___nodes ___nodes
} }
}) })
@@ -154,7 +154,7 @@ impl ToTokens for BodyNode {
if is_if_chain_terminated(chain) { if is_if_chain_terminated(chain) {
tokens.append_all(quote! { tokens.append_all(quote! {
{ {
let ___nodes = (#chain).into_dyn_node(__cx); let ___nodes = (#chain).into_dyn_node();
___nodes ___nodes
} }
}); });
@@ -211,7 +211,7 @@ impl ToTokens for BodyNode {
tokens.append_all(quote! { tokens.append_all(quote! {
{ {
let ___nodes = (#body).into_dyn_node(__cx); let ___nodes = (#body).into_dyn_node();
___nodes ___nodes
} }
}); });
+4 -5
View File
@@ -38,21 +38,20 @@ pub(crate) fn get_effect_stack() -> EffectStack {
/// Create a new effect. The effect will be run immediately and whenever any signal it reads changes. /// Create a new effect. The effect will be run immediately and whenever any signal it reads changes.
/// The signal will be owned by the current component and will be dropped when the component is dropped. /// The signal will be owned by the current component and will be dropped when the component is dropped.
pub fn use_effect(cx: &ScopeState, callback: impl FnMut() + 'static) { pub fn use_effect(callback: impl FnMut() + 'static) {
cx.use_hook(|| Effect::new(callback)); once(|| Effect::new(callback));
} }
/// Create a new effect. The effect will be run immediately and whenever any signal it reads changes. /// Create a new effect. The effect will be run immediately and whenever any signal it reads changes.
/// The signal will be owned by the current component and will be dropped when the component is dropped. /// The signal will be owned by the current component and will be dropped when the component is dropped.
pub fn use_effect_with_dependencies<D: Dependency>( pub fn use_effect_with_dependencies<D: Dependency>(
cx: &ScopeState,
dependencies: D, dependencies: D,
mut callback: impl FnMut(D::Out) + 'static, mut callback: impl FnMut(D::Out) + 'static,
) where ) where
D::Out: 'static, D::Out: 'static,
{ {
let dependencies_signal = use_signal(cx, || dependencies.out()); let dependencies_signal = use_signal(|| dependencies.out());
cx.use_hook(|| { once(|| {
Effect::new(move || { Effect::new(move || {
let deref = &*dependencies_signal.read(); let deref = &*dependencies_signal.read();
callback(deref.clone()); callback(deref.clone());
+1 -1
View File
@@ -44,7 +44,7 @@ fn owner_in_scope(scope: ScopeId) -> Rc<Owner> {
Some(rt) => rt, Some(rt) => rt,
None => { None => {
let owner = Rc::new(current_store().owner()); let owner = Rc::new(current_store().owner());
provide_context_to_scope(scope, owner).expect("in a virtual dom") scope.provide_context(owner).expect("in a virtual dom")
} }
} }
} }
+5 -9
View File
@@ -22,11 +22,8 @@ use crate::{get_effect_stack, signal::SignalData, CopyValue, Effect, ReadOnlySig
/// } /// }
/// ``` /// ```
#[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"] #[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"]
pub fn use_selector<R: PartialEq>( pub fn use_selector<R: PartialEq>(f: impl FnMut() -> R + 'static) -> ReadOnlySignal<R> {
cx: &ScopeState, once(|| selector(f))
f: impl FnMut() -> R + 'static,
) -> ReadOnlySignal<R> {
*cx.use_hook(|| selector(f))
} }
/// Creates a new Selector with some local dependencies. The selector will be run immediately and whenever any signal it reads or any dependencies it tracks changes /// Creates a new Selector with some local dependencies. The selector will be run immediately and whenever any signal it reads or any dependencies it tracks changes
@@ -47,15 +44,14 @@ pub fn use_selector<R: PartialEq>(
/// ``` /// ```
#[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"] #[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"]
pub fn use_selector_with_dependencies<R: PartialEq, D: Dependency>( pub fn use_selector_with_dependencies<R: PartialEq, D: Dependency>(
cx: &ScopeState,
dependencies: D, dependencies: D,
mut f: impl FnMut(D::Out) -> R + 'static, mut f: impl FnMut(D::Out) -> R + 'static,
) -> ReadOnlySignal<R> ) -> ReadOnlySignal<R>
where where
D::Out: 'static, D::Out: 'static,
{ {
let dependencies_signal = use_signal(cx, || dependencies.out()); let dependencies_signal = use_signal(|| dependencies.out());
let selector = *cx.use_hook(|| { let selector = once(|| {
selector(move || { selector(move || {
let deref = &*dependencies_signal.read(); let deref = &*dependencies_signal.read();
f(deref.clone()) f(deref.clone())
@@ -87,9 +83,9 @@ pub fn selector<R: PartialEq>(mut f: impl FnMut() -> R + 'static) -> ReadOnlySig
state.inner.value.set(SignalData { state.inner.value.set(SignalData {
subscribers: Default::default(), subscribers: Default::default(),
effect_subscribers: Default::default(), effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"),
value: f(), value: f(),
effect_stack: get_effect_stack(), effect_stack: get_effect_stack(),
update_any: schedule_update_any(),
}); });
{ {
get_effect_stack().effects.write().pop(); get_effect_stack().effects.write().pop();
+7 -6
View File
@@ -7,8 +7,9 @@ use std::{
}; };
use dioxus_core::{ use dioxus_core::{
once,
prelude::{current_scope_id, has_context, provide_context, schedule_update_any}, prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
ScopeId, ScopeState, ScopeId,
}; };
use generational_box::{GenerationalRef, GenerationalRefMut}; use generational_box::{GenerationalRef, GenerationalRefMut};
@@ -47,11 +48,11 @@ use crate::{get_effect_stack, CopyValue, Effect, EffectStack};
/// ``` /// ```
#[track_caller] #[track_caller]
#[must_use] #[must_use]
pub fn use_signal<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal<T> { pub fn use_signal<T: 'static>(f: impl FnOnce() -> T) -> Signal<T> {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
let caller = std::panic::Location::caller(); let caller = std::panic::Location::caller();
*cx.use_hook(|| { once(|| {
Signal::new_with_caller( Signal::new_with_caller(
f(), f(),
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@@ -155,7 +156,7 @@ impl<T: 'static> Signal<T> {
inner: CopyValue::new(SignalData { inner: CopyValue::new(SignalData {
subscribers: Default::default(), subscribers: Default::default(),
effect_subscribers: Default::default(), effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"), update_any: schedule_update_any(),
value, value,
effect_stack: get_effect_stack(), effect_stack: get_effect_stack(),
}), }),
@@ -172,7 +173,7 @@ impl<T: 'static> Signal<T> {
SignalData { SignalData {
subscribers: Default::default(), subscribers: Default::default(),
effect_subscribers: Default::default(), effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"), update_any: schedule_update_any(),
value, value,
effect_stack: get_effect_stack(), effect_stack: get_effect_stack(),
}, },
@@ -189,7 +190,7 @@ impl<T: 'static> Signal<T> {
SignalData { SignalData {
subscribers: Default::default(), subscribers: Default::default(),
effect_subscribers: Default::default(), effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"), update_any: schedule_update_any(),
value, value,
effect_stack: get_effect_stack(), effect_stack: get_effect_stack(),
}, },