Merge branch 'master' into jk/breaking-merged
This commit is contained in:
211 files changed
+21135
-3770
No files matched your search
@@ -7,7 +7,7 @@ use crate::{
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
backtrace::Backtrace,
|
||||
cell::RefCell,
|
||||
cell::{Cell, RefCell},
|
||||
error::Error,
|
||||
fmt::{Debug, Display},
|
||||
rc::Rc,
|
||||
|
||||
@@ -568,6 +568,51 @@ pub enum TemplateAttribute {
|
||||
},
|
||||
}
|
||||
|
||||
/// An attribute with information about its position in the DOM and the element it was mounted to
|
||||
#[derive(Debug)]
|
||||
pub struct MountedAttribute<'a> {
|
||||
pub(crate) ty: AttributeType<'a>,
|
||||
|
||||
/// The element in the DOM that this attribute belongs to
|
||||
pub(crate) mounted_element: Cell<ElementId>,
|
||||
}
|
||||
|
||||
impl<'a> From<Attribute<'a>> for MountedAttribute<'a> {
|
||||
fn from(attr: Attribute<'a>) -> Self {
|
||||
Self {
|
||||
ty: AttributeType::Single(attr),
|
||||
mounted_element: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [Attribute<'a>]> for MountedAttribute<'a> {
|
||||
fn from(attr: &'a [Attribute<'a>]) -> Self {
|
||||
Self {
|
||||
ty: AttributeType::Many(attr),
|
||||
mounted_element: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Vec<Attribute<'a>>> for MountedAttribute<'a> {
|
||||
fn from(attr: &'a Vec<Attribute<'a>>) -> Self {
|
||||
attr.as_slice().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MountedAttribute<'a> {
|
||||
/// Get the type of this attribute
|
||||
pub fn attribute_type(&self) -> &AttributeType<'a> {
|
||||
&self.ty
|
||||
}
|
||||
|
||||
/// Get the element that this attribute is mounted to
|
||||
pub fn mounted_element(&self) -> ElementId {
|
||||
self.mounted_element.get()
|
||||
}
|
||||
}
|
||||
|
||||
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
|
||||
#[derive(Debug)]
|
||||
pub struct Attribute {
|
||||
@@ -875,3 +920,15 @@ impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for anything that has a dynamic list of attributes
|
||||
pub trait HasAttributes<'a> {
|
||||
/// Push an attribute onto the list of attributes
|
||||
fn push_attribute(
|
||||
self,
|
||||
name: &'a str,
|
||||
ns: Option<&'static str>,
|
||||
attr: impl IntoAttributeValue<'a>,
|
||||
volatile: bool,
|
||||
) -> Self;
|
||||
}
|
||||
@@ -95,6 +95,16 @@ impl Runtime {
|
||||
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, not public.
|
||||
pub(crate) fn with_scope<O>(&self, id: ScopeId, f: impl FnOnce() -> O) -> O {
|
||||
self.scope_stack.borrow_mut().push(id);
|
||||
let o = f();
|
||||
self.scope_stack.borrow_mut().pop();
|
||||
o
|
||||
}
|
||||
|
||||
/// 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
|
||||
@@ -145,6 +155,17 @@ impl RuntimeGuard {
|
||||
push_runtime(runtime.clone());
|
||||
Self(runtime)
|
||||
}
|
||||
|
||||
/// Run a function with a given runtime and scope in context
|
||||
pub fn with<O>(runtime: Rc<Runtime>, scope: Option<ScopeId>, f: impl FnOnce() -> O) -> O {
|
||||
let guard = Self::new(runtime.clone());
|
||||
let o = match scope {
|
||||
Some(scope) => Runtime::with_scope(&runtime, scope, f),
|
||||
None => f(),
|
||||
};
|
||||
drop(guard);
|
||||
o
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RuntimeGuard {
|
||||
|
||||
@@ -114,7 +114,7 @@ impl ScopeContext {
|
||||
}
|
||||
|
||||
let mut search_parent = self.parent_id;
|
||||
let context = with_runtime(|runtime: &crate::runtime::Runtime| {
|
||||
let cur_runtime = with_runtime(|runtime: &crate::runtime::Runtime| {
|
||||
while let Some(parent_id) = search_parent {
|
||||
let parent = runtime.get_context(parent_id).unwrap();
|
||||
tracing::trace!(
|
||||
@@ -132,9 +132,9 @@ impl ScopeContext {
|
||||
search_parent = parent.parent_id;
|
||||
}
|
||||
None
|
||||
})
|
||||
.flatten();
|
||||
match context {
|
||||
});
|
||||
|
||||
match cur_runtime.flatten() {
|
||||
Some(ctx) => Some(ctx),
|
||||
None => {
|
||||
tracing::trace!(
|
||||
@@ -389,3 +389,15 @@ impl ScopeId {
|
||||
with_scope(self, |cx| cx.height()).expect("to be in a dioxus runtime")
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a future on a component given its [`ScopeId`].
|
||||
pub fn spawn_at(fut: impl Future<Output = ()> + 'static, scope_id: ScopeId) -> Option<TaskId> {
|
||||
with_runtime(|rt| rt.get_context(scope_id).unwrap().push_future(fut))
|
||||
}
|
||||
|
||||
/// Spawn a future that Dioxus won't clean up when this component is unmounted
|
||||
///
|
||||
/// This is good for tasks that need to be run after the component has been dropped.
|
||||
pub fn spawn_forever(fut: impl Future<Output = ()> + 'static) -> Option<TaskId> {
|
||||
spawn_at(fut, ScopeId(0))
|
||||
}
|
||||
@@ -41,6 +41,7 @@ pub struct ScopeState {
|
||||
|
||||
impl Drop for ScopeState {
|
||||
fn drop(&mut self) {
|
||||
self.drop_listeners();
|
||||
self.runtime.remove_context(self.context_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc};
|
||||
/// #[component]
|
||||
/// fn Title<'a>( children: Element) -> Element {
|
||||
/// cx.render(rsx! {
|
||||
/// div { id: "title", children }
|
||||
/// div { id: "title", {children} }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
@@ -394,17 +394,14 @@ impl VirtualDom {
|
||||
let this_path = node_template.attr_paths[idx];
|
||||
|
||||
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
|
||||
if attr.name.trim_start_matches("on") == name
|
||||
&& target_path.is_decendant(&this_path)
|
||||
{
|
||||
listeners.push(&attr.value);
|
||||
|
||||
// Break if this is the exact target element.
|
||||
// This means we won't call two listeners with the same name on the same element. This should be
|
||||
// documented, or be rejected from the rsx! macro outright
|
||||
if target_path == this_path {
|
||||
break;
|
||||
}
|
||||
if target_path.is_decendant(&this_path) {
|
||||
attr.ty.for_each(|attribute| {
|
||||
if attribute.name.trim_start_matches("on") == name {
|
||||
if let AttributeValue::Listener(listener) = &attribute.value {
|
||||
listeners.push(listener);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,38 +413,67 @@ impl VirtualDom {
|
||||
listener.call(uievent.clone());
|
||||
self.runtime.rendering.set(true);
|
||||
|
||||
// let origin: ScopeId = path.scope;
|
||||
// self.runtime.scope_stack.borrow_mut().push(origin);
|
||||
// self.runtime.rendering.set(false);
|
||||
// if let Some(cb) = listener.borrow_mut().as_deref_mut() {
|
||||
// cb(uievent.clone());
|
||||
// }
|
||||
// self.runtime.scope_stack.borrow_mut().pop();
|
||||
// self.runtime.rendering.set(true);
|
||||
|
||||
if !uievent.propagates.get() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mount = el_ref.mount.get().as_usize();
|
||||
parent_node = mount.and_then(|id| self.mounts.get(id).and_then(|el| el.parent));
|
||||
}
|
||||
} else {
|
||||
// Otherwise, we just call the listener on the target element
|
||||
if let Some(path) = parent_node {
|
||||
let el_ref = &self.mounts[path.mount.0].node;
|
||||
let node_template = el_ref.template.get();
|
||||
let target_path = path.path;
|
||||
|
||||
for (idx, attr) in el_ref.dynamic_attrs.iter().enumerate() {
|
||||
let this_path = node_template.attr_paths[idx];
|
||||
|
||||
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
|
||||
// Only call the listener if this is the exact target element.
|
||||
if attr.name.trim_start_matches("on") == name && target_path == this_path {
|
||||
if let AttributeValue::Listener(listener) = &attr.value {
|
||||
self.runtime.rendering.set(false);
|
||||
listener.call(uievent.clone());
|
||||
self.runtime.rendering.set(true);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
let mount = el_ref.mount.get().as_usize();
|
||||
parent_node = mount.and_then(|id| self.mounts.get(id).and_then(|el| el.parent));
|
||||
}
|
||||
}
|
||||
|
||||
// else {
|
||||
// // Otherwise, we just call the listener on the target element
|
||||
// if let Some(path) = parent_node {
|
||||
// let el_ref = &self.mounts[path.mount.0].node;
|
||||
// let node_template = el_ref.template.get();
|
||||
// let target_path = path.path;
|
||||
|
||||
// for (idx, attr) in el_ref.dynamic_attrs.iter().enumerate() {
|
||||
// let this_path = node_template.attr_paths[idx];
|
||||
|
||||
// // Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
|
||||
// // Only call the listener if this is the exact target element.
|
||||
// if attr.name.trim_start_matches("on") == name && target_path == this_path {
|
||||
// if let AttributeValue::Listener(listener) = &attr.value {
|
||||
// self.runtime.rendering.set(false);
|
||||
// listener.call(uievent.clone());
|
||||
// self.runtime.rendering.set(true);
|
||||
|
||||
// // if target_path == this_path {
|
||||
// // let mut should_stop = false;
|
||||
// // attr.ty.for_each(|attribute| {
|
||||
// // if attribute.name.trim_start_matches("on") == name {
|
||||
// // if let AttributeValue::Listener(listener) = &attribute.value {
|
||||
// // let origin = path.scope;
|
||||
// // self.runtime.scope_stack.borrow_mut().push(origin);
|
||||
// // self.runtime.rendering.set(false);
|
||||
// // if let Some(cb) = listener.borrow_mut().as_deref_mut() {
|
||||
// // cb(uievent.clone());
|
||||
// // }
|
||||
// // self.runtime.scope_stack.borrow_mut().pop();
|
||||
// // self.runtime.rendering.set(true);
|
||||
|
||||
// should_stop = true;
|
||||
// }
|
||||
// }
|
||||
// // });
|
||||
// if should_stop {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,19 +43,19 @@ fn fragments_nested() {
|
||||
render!(
|
||||
div { "hello" }
|
||||
div { "goodbye" }
|
||||
render! {
|
||||
{render! {
|
||||
div { "hello" }
|
||||
div { "goodbye" }
|
||||
render! {
|
||||
{render! {
|
||||
div { "hello" }
|
||||
div { "goodbye" }
|
||||
render! {
|
||||
{render! {
|
||||
div { "hello" }
|
||||
div { "goodbye" }
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
))
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
@@ -77,7 +77,10 @@ fn fragments_across_components() {
|
||||
|
||||
fn demo_child() -> Element {
|
||||
let world = "world";
|
||||
render! { "hellO!", world }
|
||||
render! {
|
||||
"hellO!"
|
||||
{world}
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
@@ -91,7 +94,7 @@ fn list_fragments() {
|
||||
fn app() -> Element {
|
||||
render!(
|
||||
h1 { "hello" }
|
||||
(0..6).map(|f| render!( span { "{f}" }))
|
||||
{(0..6).map(|f| render!( span { "{f}" }))}
|
||||
)
|
||||
}
|
||||
assert_eq!(
|
||||
|
||||
@@ -11,12 +11,12 @@ use dioxus_core::ElementId;
|
||||
fn app() -> Element {
|
||||
render! {
|
||||
div {
|
||||
(0..3).map(|i| render! {
|
||||
for i in 0..3 {
|
||||
div {
|
||||
h1 { "hello world! "}
|
||||
p { "{i}" }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ fn nested_passthru_creates() {
|
||||
|
||||
#[component]
|
||||
fn PassThru(children: Element) -> Element {
|
||||
render!(children)
|
||||
render!({ children })
|
||||
}
|
||||
|
||||
let mut dom = VirtualDom::new(app);
|
||||
@@ -54,7 +54,7 @@ fn nested_passthru_creates_add() {
|
||||
|
||||
#[component]
|
||||
fn ChildComp(children: Element) -> Element {
|
||||
render! {children}
|
||||
render! {{children}}
|
||||
}
|
||||
|
||||
let mut dom = VirtualDom::new(app);
|
||||
|
||||
@@ -39,7 +39,7 @@ fn component_swap() {
|
||||
|
||||
fn nav_bar() -> Element {
|
||||
render! {
|
||||
h1 { "NavBar", (0..3).map(|_| render!(nav_link {})) }
|
||||
h1 { "NavBar", {(0..3).map(|_| render!(nav_link {}))} }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ fn keyed_diffing_out_of_order() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
{
|
||||
@@ -59,7 +59,7 @@ fn keyed_diffing_out_of_order_adds() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
_ = dom.rebuild(&mut dioxus_core::NoOpMutations);
|
||||
@@ -85,7 +85,7 @@ fn keyed_diffing_out_of_order_adds_3() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
_ = dom.rebuild(&mut dioxus_core::NoOpMutations);
|
||||
@@ -111,7 +111,7 @@ fn keyed_diffing_out_of_order_adds_4() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
_ = dom.rebuild(&mut dioxus_core::NoOpMutations);
|
||||
@@ -137,7 +137,7 @@ fn keyed_diffing_out_of_order_adds_5() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
_ = dom.rebuild(&mut dioxus_core::NoOpMutations);
|
||||
@@ -162,7 +162,7 @@ fn keyed_diffing_additions() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
_ = dom.rebuild(&mut dioxus_core::NoOpMutations);
|
||||
@@ -187,7 +187,7 @@ fn keyed_diffing_additions_and_moves_on_ends() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
_ = dom.rebuild(&mut dioxus_core::NoOpMutations);
|
||||
@@ -216,7 +216,7 @@ fn keyed_diffing_additions_and_moves_in_middle() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
_ = dom.rebuild(&mut dioxus_core::NoOpMutations);
|
||||
@@ -250,7 +250,7 @@ fn controlled_keyed_diffing_out_of_order() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
_ = dom.rebuild(&mut dioxus_core::NoOpMutations);
|
||||
@@ -284,7 +284,7 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
_ = dom.rebuild(&mut dioxus_core::NoOpMutations);
|
||||
@@ -313,7 +313,7 @@ fn remove_list() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
_ = dom.rebuild(&mut dioxus_core::NoOpMutations);
|
||||
@@ -338,7 +338,7 @@ fn no_common_keys() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render!(order.iter().map(|i| render!(div { key: "{i}" })))
|
||||
render!({ order.iter().map(|i| render!(div { key: "{i}" })) })
|
||||
});
|
||||
|
||||
_ = dom.rebuild(&mut dioxus_core::NoOpMutations);
|
||||
|
||||
@@ -9,9 +9,9 @@ fn list_creates_one_by_one() {
|
||||
|
||||
render! {
|
||||
div {
|
||||
(0..gen).map(|i| render! {
|
||||
for i in 0..gen {
|
||||
div { "{i}" }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -78,9 +78,9 @@ fn removes_one_by_one() {
|
||||
|
||||
render! {
|
||||
div {
|
||||
(0..gen).map(|i| render! {
|
||||
for i in 0..gen {
|
||||
div { "{i}" }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -153,10 +153,10 @@ fn list_shrink_multiroot() {
|
||||
let mut dom = VirtualDom::new(|| {
|
||||
render! {
|
||||
div {
|
||||
(0..generation()).map(|i| render! {
|
||||
for i in 0..cx.generation() {
|
||||
div { "{i}" }
|
||||
div { "{i}" }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -214,10 +214,10 @@ fn removes_one_by_one_multiroot() {
|
||||
|
||||
render! {
|
||||
div {
|
||||
(0..gen).map(|i| render! {
|
||||
{(0..gen).map(|i| rsx! {
|
||||
div { "{i}" }
|
||||
div { "{i}" }
|
||||
})
|
||||
})}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -276,9 +276,9 @@ fn removes_one_by_one_multiroot() {
|
||||
fn two_equal_fragments_are_equal_static() {
|
||||
let mut dom = VirtualDom::new(|| {
|
||||
render! {
|
||||
(0..5).map(|_| render! {
|
||||
for _ in 0..5 {
|
||||
div { "hello" }
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -290,9 +290,9 @@ fn two_equal_fragments_are_equal_static() {
|
||||
fn two_equal_fragments_are_equal() {
|
||||
let mut dom = VirtualDom::new(|| {
|
||||
render! {
|
||||
(0..5).map(|i| render! {
|
||||
for i in 0..5 {
|
||||
div { "hello {i}" }
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -310,7 +310,11 @@ fn remove_many() {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
render! {(0..num).map(|i| render! { div { "hello {i}" } })}
|
||||
render! {
|
||||
for i in 0..num {
|
||||
div { "hello {i}" }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
|
||||
@@ -58,11 +58,11 @@ fn app() -> Element {
|
||||
*CLICKS.lock().unwrap() += 1;
|
||||
},
|
||||
|
||||
vec![
|
||||
{vec![
|
||||
render! {
|
||||
problematic_child {}
|
||||
}
|
||||
].into_iter()
|
||||
].into_iter()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +218,7 @@ fn create_random_dynamic_attr() -> Attribute {
|
||||
random_ns(),
|
||||
rand::random(),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
static mut TEMPLATE_COUNT: usize = 0;
|
||||
@@ -249,7 +250,9 @@ fn create_random_element(cx: DepthProps) -> Element {
|
||||
)
|
||||
.into_boxed_str(),
|
||||
));
|
||||
let node = VNode::new(
|
||||
println!("{template:#?}");
|
||||
let node = cx.vnode(
|
||||
None.into(),
|
||||
None,
|
||||
template,
|
||||
dynamic_node_types
|
||||
|
||||
@@ -52,7 +52,7 @@ fn events_generate() {
|
||||
"Click me!"
|
||||
}
|
||||
},
|
||||
_ => render!(()),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -38,9 +38,9 @@ fn app() -> Element {
|
||||
}
|
||||
button { onclick: move |_| idx -= 1, "-" }
|
||||
ul {
|
||||
(0..*idx()).map(|i| render! {
|
||||
{(0..**idx).map(|i| rsx! {
|
||||
ChildExample { i: i, onhover: onhover }
|
||||
})
|
||||
})}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ fn test_memory_leak() {
|
||||
spawn(async {});
|
||||
|
||||
if val == 2 || val == 4 {
|
||||
return render!(());
|
||||
return render!({});
|
||||
}
|
||||
|
||||
let mut name = once(|| String::from("numbers: "));
|
||||
@@ -73,7 +73,7 @@ fn memo_works_properly() {
|
||||
let val = generation();
|
||||
|
||||
if val == 2 || val == 4 {
|
||||
return render!(());
|
||||
return None;
|
||||
}
|
||||
|
||||
let name = once(|| String::from("asd"));
|
||||
|
||||
Reference in new issue
Block a user