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"
syn = { version = "2.0", features = ["full", "extra-traits"] }
dioxus-rsx = { workspace = true }
dioxus-core = { workspace = true }
constcat = "0.3.0"
prettyplease = "0.2.15"
+1 -34
View File
@@ -147,9 +147,7 @@ pub mod utils;
pub use utils::DeserializerArgs;
pub use utils::DeserializerOutput;
pub use utils::TypeHelper;
use dioxus_core::{Element, Scope};
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::*;
@@ -167,12 +165,6 @@ pub struct ComponentBody {
/// 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.
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.
pub has_extra_args: bool,
}
@@ -192,31 +184,8 @@ impl ComponentBody {
impl Parse for ComponentBody {
fn parse(input: ParseStream) -> Result<Self> {
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 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();
let element_type_path = "::dioxus::core::Element";
if item_fn.sig.output == ReturnType::Default {
return Err(Error::new(
@@ -229,8 +198,6 @@ impl Parse for ComponentBody {
Ok(Self {
item_fn,
cx_arg,
cx_pat_type,
has_extra_args,
})
}
@@ -1,5 +1,4 @@
use crate::component_body::ComponentBody;
use dioxus_core::{Element, Scope};
use quote::ToTokens;
use syn::{parse_quote, Path};
@@ -36,22 +35,3 @@ where
#[allow(unused_qualifications)]
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";
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_fn = ItemFn {
@@ -34,7 +34,7 @@ fn get_out_comp_fn(orig_comp_fn: &ItemFn, cx_pat: &Pat) -> ItemFn {
#[allow(clippy::inline_always)]
#[inline(always)]
#inner_comp_fn
#inner_comp_ident (#cx_pat)
#inner_comp_ident(__props)
}
},
..orig_comp_fn.clone()
@@ -88,14 +88,9 @@ impl DeserializerArgs<ComponentDeserializerOutput> for ComponentDeserializerArgs
impl ComponentDeserializerArgs {
fn deserialize_no_props(component_body: &ComponentBody) -> ComponentDeserializerOutput {
let ComponentBody {
item_fn,
cx_pat_type,
..
} = component_body;
let cx_pat = &cx_pat_type.pat;
let ComponentBody { item_fn, .. } = component_body;
let comp_fn = get_out_comp_fn(item_fn, cx_pat);
let comp_fn = get_out_comp_fn(item_fn);
ComponentDeserializerOutput {
comp_fn,
@@ -106,12 +101,7 @@ impl ComponentDeserializerArgs {
fn deserialize_with_props(
component_body: &ComponentBody,
) -> Result<ComponentDeserializerOutput> {
let ComponentBody {
item_fn,
cx_pat_type,
..
} = component_body;
let cx_pat = &cx_pat_type.pat;
let ComponentBody { item_fn, .. } = component_body;
let comp_parsed = match parse2::<ComponentBody>(quote!(#item_fn)) {
Ok(comp_body) => comp_body,
@@ -130,7 +120,7 @@ impl ComponentDeserializerArgs {
let props_struct = inlined_props_output.props_struct;
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 {
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 {
let ComponentBody {
item_fn,
cx_pat_type,
..
} = component_body;
let ComponentBody { item_fn, .. } = component_body;
let ItemFn {
attrs: fn_attrs,
vis,
@@ -246,7 +242,6 @@ fn get_function(component_body: &ComponentBody) -> ItemFn {
} = sig;
let Generics { where_clause, .. } = generics;
let cx_pat = &cx_pat_type.pat;
let struct_ident = Ident::new(&format!("{fn_ident}Props"), fn_ident.span());
// Skip first arg since that's the context
@@ -298,10 +293,10 @@ fn get_function(component_body: &ComponentBody) -> ItemFn {
parse_quote! {
#(#fn_attrs)*
#(#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
{
let #struct_ident { #(#struct_field_names),* } = &#cx_pat.props;
let #struct_ident { #(#struct_field_names),* } = &__props;
#fn_block
}
}
+1 -1
View File
@@ -155,7 +155,7 @@ impl VirtualDom {
}
Element { .. } => {
#[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)]
let id = self.write_element_root(
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)]
/// An instance of an error captured by a descendant component.
pub struct CapturedError {
/// The error captured by the error boundary
pub error: Box<dyn Debug + 'static>,
pub error: Box<dyn AnyDebug + 'static>,
/// The backtrace of the error
pub backtrace: Backtrace,
@@ -65,8 +76,7 @@ impl CapturedError {
/// Downcast the error type into a concrete error type
pub fn downcast<T: 'static>(&self) -> Option<&T> {
if TypeId::of::<T>() == self.error.type_id() {
let raw = self.error.as_ref() as *const _ as *const T;
Some(unsafe { &*raw })
self.error.as_any().downcast_ref::<T>()
} else {
None
}
@@ -100,14 +110,9 @@ impl ErrorBoundary {
}
/// Push an error into this Error Boundary
pub fn insert_error(
&self,
scope: ScopeId,
error: Box<dyn Debug + 'static>,
backtrace: Backtrace,
) {
pub fn insert_error(&self, scope: ScopeId, error: impl Debug + 'static, backtrace: Backtrace) {
self.inner.error.replace(Some(CapturedError {
error,
error: Box::new(error),
scope,
backtrace,
}));
+20
View File
@@ -1,3 +1,5 @@
use std::sync::Arc;
use futures_util::Future;
use crate::{
@@ -113,3 +115,21 @@ pub fn parent_scope() -> Option<ScopeId> {
pub fn 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::{
fc_to_builder, generation, once, vdom_is_rendering, AnyValue, Attribute, AttributeValue,
CapturedError, Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode,
Mutation, MutationsVec, Properties, RenderReturn, ScopeId, Task, Template, TemplateAttribute,
TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom, WriteMutations,
fc_to_builder, generation, once, schedule_update, schedule_update_any, vdom_is_rendering,
AnyValue, Attribute, AttributeValue, CapturedError, Component, DynamicNode, Element, ElementId,
Event, Fragment, IntoDynNode, Mutation, MutationsVec, Properties, RenderReturn, ScopeId, Task,
Template, TemplateAttribute, TemplateNode, VComponent, VNode, VPlaceholder, VText, VirtualDom,
WriteMutations,
};
/// The purpose of this module is to alleviate imports of many common types
@@ -87,9 +88,9 @@ pub mod prelude {
pub use crate::innerlude::{
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,
push_future, remove_future, spawn, spawn_forever, suspend, use_error_boundary, AnyValue,
Component, Element, ErrorBoundary, Event, EventHandler, Fragment, IntoAttributeValue,
IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, Task, Template, TemplateAttribute,
TemplateNode, Throw, VNode, VirtualDom,
push_future, remove_future, schedule_update, schedule_update_any, spawn, spawn_forever,
suspend, use_error_boundary, AnyValue, Component, Element, ErrorBoundary, Event,
EventHandler, Fragment, IntoAttributeValue, IntoDynNode, Properties, Runtime, RuntimeGuard,
ScopeId, Task, Template, TemplateAttribute, TemplateNode, Throw, VNode, VirtualDom,
};
}
+28
View File
@@ -9,6 +9,7 @@ use std::{
cell::{Cell, RefCell},
future::Future,
rc::Rc,
sync::Arc,
};
/// A component's state separate from its props.
@@ -69,6 +70,26 @@ impl ScopeContext {
.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
pub fn has_context<T: 'static + Clone>(&self) -> Option<T> {
self.shared_contexts
@@ -355,4 +376,11 @@ impl ScopeId {
pub fn needs_update(self) {
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) {
self.flush_templates(to);
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
RenderReturn::Ready(node) => {
let m = self.create_scope(ScopeId::ROOT, &node, to);
+7 -6
View File
@@ -2,6 +2,7 @@
use async_trait::async_trait;
use dioxus_core::prelude::consume_context;
use dioxus_core::prelude::provide_context;
use dioxus_core::ScopeId;
use dioxus_html::prelude::{EvalError, EvalProvider, Evaluator};
use std::{cell::RefCell, rc::Rc};
@@ -9,13 +10,13 @@ use crate::{query::Query, DesktopContext};
/// Provides the DesktopEvalProvider through [`cx.provide_context`].
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 });
provide_context(provider);
ScopeId::ROOT.provide_context(provider);
}
/// Reprents the desktop-target's provider of evaluators.
pub struct DesktopEvalProvider {
/// Represents the desktop-target's provider of evaluators.
pub(crate) struct DesktopEvalProvider {
desktop_ctx: DesktopContext,
}
@@ -25,8 +26,8 @@ impl EvalProvider for DesktopEvalProvider {
}
}
/// Reprents a desktop-target's JavaScript evaluator.
pub struct DesktopEvaluator {
/// Represents a desktop-target's JavaScript evaluator.
pub(crate) struct DesktopEvaluator {
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;
pub use use_on_destroy::*;
// mod use_on_destroy;
// pub use use_on_destroy::*;
mod use_const;
pub use use_const::*;
// mod use_const;
// pub use use_const::*;
mod use_context;
pub use use_context::*;
// mod use_context;
// pub use use_context::*;
mod use_state;
pub use use_state::{use_state, UseState};
// mod use_state;
// pub use use_state::{use_state, UseState};
mod use_ref;
pub use use_ref::*;
// mod use_ref;
// pub use use_ref::*;
mod use_shared_state;
pub use use_shared_state::*;
// mod use_shared_state;
// pub use use_shared_state::*;
mod use_coroutine;
pub use use_coroutine::*;
// mod use_coroutine;
// pub use use_coroutine::*;
mod use_future;
pub use use_future::*;
// mod use_future;
// pub use use_future::*;
mod use_effect;
pub use use_effect::*;
// mod use_effect;
// pub use use_effect::*;
mod use_callback;
pub use use_callback::*;
// mod use_callback;
// pub use use_callback::*;
mod use_memo;
pub use use_memo::*;
// mod use_memo;
// pub use use_memo::*;
mod use_on_create;
pub use use_on_create::*;
mod use_root_context;
pub use use_root_context::*;
// mod use_on_create;
// pub use use_on_create::*;
// mod 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 = quote! { #value };
quote! {
__cx.attr(
::dioxus::core::Attribute::new(
#attribute,
#value,
#ns,
@@ -79,7 +79,7 @@ impl ToTokens for ElementAttrNamed {
ElementAttrValue::EventTokens(tokens) => match &self.attr.name {
ElementAttrName::BuiltIn(name) => {
quote! {
dioxus_elements::events::#name(__cx, #tokens)
dioxus_elements::events::#name(#tokens)
}
}
ElementAttrName::Custom(_) => todo!(),
+4 -4
View File
@@ -189,11 +189,11 @@ impl ToTokens for Component {
};
tokens.append_all(quote! {
__cx.component(
::dioxus::core::DynamicNode::Component(::dioxus::core::VComponent::new(
#gen_name,
#builder,
#fn_name
)
))
})
}
}
@@ -217,10 +217,10 @@ impl ToTokens for ContentField {
match self {
ContentField::ManExpr(e) => e.to_tokens(tokens),
ContentField::Formatted(s) => tokens.append_all(quote! {
__cx.raw_text(#s)
#s.to_string()
}),
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 { .. }));
tokens.append_all(quote! {
__cx.element(
::dioxus::core::Element::new(
#name,
__cx.bump().alloc([ #(#listeners),* ]),
__cx.bump().alloc([ #(#attr),* ]),
__cx.bump().alloc([ #(#children),* ]),
vec![ #(#listeners),* ],
vec![ #(#attr),* ],
vec![ #(#children),* ],
#key,
)
});
+7 -8
View File
@@ -69,7 +69,7 @@ impl CallBody {
&self,
template: Option<CallBody>,
location: &'static str,
) -> Option<Template<'static>> {
) -> Option<Template> {
let mut renderer: TemplateRenderer = TemplateRenderer {
roots: &self.roots,
location: None,
@@ -138,7 +138,6 @@ impl ToTokens for RenderCallBody {
out_tokens.append_all(quote! {
Some({
let __cx = cx;
#body
})
})
@@ -156,7 +155,7 @@ impl<'a> TemplateRenderer<'a> {
&mut self,
previous_call: Option<CallBody>,
location: &'static str,
) -> Option<Template<'static>> {
) -> Option<Template> {
let mut mapping = previous_call.map(|call| DynamicMapping::from(call.roots));
let mut context = DynamicContext::default();
@@ -202,7 +201,7 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
};
let key_tokens = match key {
Some(tok) => quote! { Some( __cx.raw_text(#tok) ) },
Some(tok) => quote! { Some( #tok.to_string() ) },
None => quote! { None },
};
@@ -257,9 +256,9 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
::dioxus::core::VNode::new(
#key_tokens,
TEMPLATE,
dioxus::core::exports::bumpalo::collections::Vec::with_capacity_in(#root_count, __cx.bump()),
__cx.bump().alloc([ #( #node_printer ),* ]),
__cx.bump().alloc([ #( #dyn_attr_printer ),* ]),
Vec::with_capacity(#root_count),
vec![ #( #node_printer ),* ],
vec![ #( #dyn_attr_printer ),* ],
)
});
}
@@ -360,7 +359,7 @@ impl<'a> DynamicContext<'a> {
&mut self,
root: &'a BodyNode,
mapping: &mut Option<DynamicMapping>,
) -> Option<TemplateNode<'static>> {
) -> Option<TemplateNode> {
match root {
BodyNode::Element(el) => {
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::Component(comp) => comp.to_tokens(tokens),
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! {
{
let ___nodes = (#exp).into_dyn_node(__cx);
let ___nodes = (#exp).into_dyn_node();
___nodes
}
}),
@@ -145,7 +145,7 @@ impl ToTokens for BodyNode {
// And then we can return them into the dyn loop
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
}
})
@@ -154,7 +154,7 @@ impl ToTokens for BodyNode {
if is_if_chain_terminated(chain) {
tokens.append_all(quote! {
{
let ___nodes = (#chain).into_dyn_node(__cx);
let ___nodes = (#chain).into_dyn_node();
___nodes
}
});
@@ -211,7 +211,7 @@ impl ToTokens for BodyNode {
tokens.append_all(quote! {
{
let ___nodes = (#body).into_dyn_node(__cx);
let ___nodes = (#body).into_dyn_node();
___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.
/// 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) {
cx.use_hook(|| Effect::new(callback));
pub fn use_effect(callback: impl FnMut() + 'static) {
once(|| Effect::new(callback));
}
/// 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.
pub fn use_effect_with_dependencies<D: Dependency>(
cx: &ScopeState,
dependencies: D,
mut callback: impl FnMut(D::Out) + 'static,
) where
D::Out: 'static,
{
let dependencies_signal = use_signal(cx, || dependencies.out());
cx.use_hook(|| {
let dependencies_signal = use_signal(|| dependencies.out());
once(|| {
Effect::new(move || {
let deref = &*dependencies_signal.read();
callback(deref.clone());
+1 -1
View File
@@ -44,7 +44,7 @@ fn owner_in_scope(scope: ScopeId) -> Rc<Owner> {
Some(rt) => rt,
None => {
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"]
pub fn use_selector<R: PartialEq>(
cx: &ScopeState,
f: impl FnMut() -> R + 'static,
) -> ReadOnlySignal<R> {
*cx.use_hook(|| selector(f))
pub fn use_selector<R: PartialEq>(f: impl FnMut() -> R + 'static) -> ReadOnlySignal<R> {
once(|| 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
@@ -47,15 +44,14 @@ pub fn use_selector<R: PartialEq>(
/// ```
#[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"]
pub fn use_selector_with_dependencies<R: PartialEq, D: Dependency>(
cx: &ScopeState,
dependencies: D,
mut f: impl FnMut(D::Out) -> R + 'static,
) -> ReadOnlySignal<R>
where
D::Out: 'static,
{
let dependencies_signal = use_signal(cx, || dependencies.out());
let selector = *cx.use_hook(|| {
let dependencies_signal = use_signal(|| dependencies.out());
let selector = once(|| {
selector(move || {
let deref = &*dependencies_signal.read();
f(deref.clone())
@@ -87,9 +83,9 @@ pub fn selector<R: PartialEq>(mut f: impl FnMut() -> R + 'static) -> ReadOnlySig
state.inner.value.set(SignalData {
subscribers: Default::default(),
effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"),
value: f(),
effect_stack: get_effect_stack(),
update_any: schedule_update_any(),
});
{
get_effect_stack().effects.write().pop();
+7 -6
View File
@@ -7,8 +7,9 @@ use std::{
};
use dioxus_core::{
once,
prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
ScopeId, ScopeState,
ScopeId,
};
use generational_box::{GenerationalRef, GenerationalRefMut};
@@ -47,11 +48,11 @@ use crate::{get_effect_stack, CopyValue, Effect, EffectStack};
/// ```
#[track_caller]
#[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)]
let caller = std::panic::Location::caller();
*cx.use_hook(|| {
once(|| {
Signal::new_with_caller(
f(),
#[cfg(debug_assertions)]
@@ -155,7 +156,7 @@ impl<T: 'static> Signal<T> {
inner: CopyValue::new(SignalData {
subscribers: Default::default(),
effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"),
update_any: schedule_update_any(),
value,
effect_stack: get_effect_stack(),
}),
@@ -172,7 +173,7 @@ impl<T: 'static> Signal<T> {
SignalData {
subscribers: Default::default(),
effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"),
update_any: schedule_update_any(),
value,
effect_stack: get_effect_stack(),
},
@@ -189,7 +190,7 @@ impl<T: 'static> Signal<T> {
SignalData {
subscribers: Default::default(),
effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"),
update_any: schedule_update_any(),
value,
effect_stack: get_effect_stack(),
},