Skip to content

Commit 67dc6e6

Browse files
authored
feat: implement type magic to allow strings, format args, and other types directly in rsx (DioxusLabs#550)
* feat: implement type magic * chore: undo example * fix: let tests pass * chore: add generic to allow any nesting of iterators * Chore: remove comments * chore: update rsx usage * chore: use cleaner version of generic IntoVnode * chore: don't derive default for lfietimed thing * chore: remove latent comment * fix: accept a third parameter
1 parent 38e8745 commit 67dc6e6

File tree

7 files changed

+80
-100
lines changed

7 files changed

+80
-100
lines changed

examples/rsx_usage.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,8 @@ fn app(cx: Scope) -> Element {
203203
self::lowercase_helper {}
204204

205205
// helper functions
206-
// Single values must be wrapped in braces or `Some` to satisfy `IntoIterator`
207-
[helper(&cx, "hello world!")]
206+
// Anything that implements IntoVnode can be dropped directly into Rsx
207+
helper(&cx, "hello world!")
208208
}
209209
})
210210
}

examples/simple_list.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use dioxus::prelude::*;
2+
3+
fn main() {
4+
dioxus_desktop::launch(app);
5+
}
6+
7+
fn app(cx: Scope) -> Element {
8+
cx.render(rsx!(
9+
// Use Map directly to lazily pull elements
10+
(0..10).map(|f| rsx! { "{f}" }),
11+
// Collect into an intermediate collection if necessary
12+
["a", "b", "c"]
13+
.into_iter()
14+
.map(|f| rsx! { "{f}" })
15+
.collect::<Vec<_>>(),
16+
// Use optionals
17+
Some(rsx! { "Some" }),
18+
))
19+
}

packages/autofmt/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ mod util;
1616
///
1717
/// Note that this is tailored to VSCode's TextEdit API and not a general Diff API. Line numbers are not accurate if
1818
/// multiple edits are applied in a single file without tracking text shifts.
19-
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq, Hash)]
19+
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq, Eq, Hash)]
2020
pub struct FormattedBlock {
2121
/// The new contents of the block
2222
pub formatted: String,

packages/core/src/lazynodes.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -288,16 +288,14 @@ mod tests {
288288
}
289289

290290
let caller = {
291-
let it = (0..10)
292-
.map(|i| {
293-
let val = cx.props.inner.clone();
294-
LazyNodes::new(move |f| {
295-
eprintln!("hell closure");
296-
let inner = DropInner { id: i };
297-
f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
298-
})
291+
let it = (0..10).map(|i| {
292+
let val = cx.props.inner.clone();
293+
LazyNodes::new(move |f| {
294+
eprintln!("hell closure");
295+
let inner = DropInner { id: i };
296+
f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
299297
})
300-
.collect::<Vec<_>>();
298+
});
301299

302300
LazyNodes::new(|f| {
303301
eprintln!("main closure");

packages/core/src/nodes.rs

Lines changed: 49 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
//!
33
//! VNodes represent lazily-constructed VDom trees that support diffing and event handlers. These VNodes should be *very*
44
//! cheap and *very* fast to construct - building a full tree should be quick.
5-
65
use crate::{
76
innerlude::{AttributeValue, ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState},
87
lazynodes::LazyNodes,
@@ -375,7 +374,6 @@ pub struct Listener<'bump> {
375374

376375
pub type InternalHandler<'bump> = &'bump RefCell<Option<InternalListenerCallback<'bump>>>;
377376
type InternalListenerCallback<'bump> = BumpBox<'bump, dyn FnMut(AnyEvent) + 'bump>;
378-
379377
type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
380378

381379
/// The callback type generated by the `rsx!` macro when an `on` field is specified for components.
@@ -706,43 +704,11 @@ impl<'a> NodeFactory<'a> {
706704
}
707705

708706
/// Create a new [`VNode::Fragment`] from any iterator
709-
pub fn fragment_from_iter<'b, 'c>(
707+
pub fn fragment_from_iter<'c, I, J>(
710708
self,
711-
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
709+
node_iter: impl IntoVNode<'a, I, J> + 'c,
712710
) -> VNode<'a> {
713-
let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
714-
715-
for node in node_iter {
716-
nodes.push(node.into_vnode(self));
717-
}
718-
719-
if nodes.is_empty() {
720-
VNode::Placeholder(self.bump.alloc(VPlaceholder { id: empty_cell() }))
721-
} else {
722-
let children = nodes.into_bump_slice();
723-
724-
if cfg!(debug_assertions)
725-
&& children.len() > 1
726-
&& children.last().unwrap().key().is_none()
727-
{
728-
// todo: make the backtrace prettier or remove it altogether
729-
log::error!(
730-
r#"
731-
Warning: Each child in an array or iterator should have a unique "key" prop.
732-
Not providing a key will lead to poor performance with lists.
733-
See docs.rs/dioxus for more information.
734-
-------------
735-
{:?}
736-
"#,
737-
backtrace::Backtrace::new()
738-
);
739-
}
740-
741-
VNode::Fragment(self.bump.alloc(VFragment {
742-
children,
743-
key: None,
744-
}))
745-
}
711+
node_iter.into_vnode(self)
746712
}
747713

748714
/// Create a new [`VNode`] from any iterator of children
@@ -800,20 +766,11 @@ impl Debug for NodeFactory<'_> {
800766
///
801767
/// As such, all node creation must go through the factory, which is only available in the component context.
802768
/// These strict requirements make it possible to manage lifetimes and state.
803-
pub trait IntoVNode<'a> {
769+
pub trait IntoVNode<'a, I = (), J = ()> {
804770
/// Convert this into a [`VNode`], using the [`NodeFactory`] as a source of allocation
805771
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
806772
}
807773

808-
// For the case where a rendered VNode is passed into the rsx! macro through curly braces
809-
impl<'a> IntoIterator for VNode<'a> {
810-
type Item = VNode<'a>;
811-
type IntoIter = std::iter::Once<Self::Item>;
812-
fn into_iter(self) -> Self::IntoIter {
813-
std::iter::once(self)
814-
}
815-
}
816-
817774
// TODO: do we even need this? It almost seems better not to
818775
// // For the case where a rendered VNode is passed into the rsx! macro through curly braces
819776
impl<'a> IntoVNode<'a> for VNode<'a> {
@@ -825,37 +782,7 @@ impl<'a> IntoVNode<'a> for VNode<'a> {
825782
// Conveniently, we also support "null" (nothing) passed in
826783
impl IntoVNode<'_> for () {
827784
fn into_vnode(self, cx: NodeFactory) -> VNode {
828-
cx.fragment_from_iter(None as Option<VNode>)
829-
}
830-
}
831-
832-
// Conveniently, we also support "None"
833-
impl IntoVNode<'_> for Option<()> {
834-
fn into_vnode(self, cx: NodeFactory) -> VNode {
835-
cx.fragment_from_iter(None as Option<VNode>)
836-
}
837-
}
838-
839-
impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
840-
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
841-
self.unwrap_or_else(|| cx.fragment_from_iter(None as Option<VNode>))
842-
}
843-
}
844-
845-
impl<'a> IntoVNode<'a> for Option<LazyNodes<'a, '_>> {
846-
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
847-
match self {
848-
Some(lazy) => lazy.call(cx),
849-
None => VNode::Placeholder(cx.bump.alloc(VPlaceholder { id: empty_cell() })),
850-
}
851-
}
852-
}
853-
854-
impl<'a, 'b> IntoIterator for LazyNodes<'a, 'b> {
855-
type Item = LazyNodes<'a, 'b>;
856-
type IntoIter = std::iter::Once<Self::Item>;
857-
fn into_iter(self) -> Self::IntoIter {
858-
std::iter::once(self)
785+
VNode::Placeholder(cx.bump.alloc(VPlaceholder { id: empty_cell() }))
859786
}
860787
}
861788

@@ -883,17 +810,53 @@ impl IntoVNode<'_> for Arguments<'_> {
883810
}
884811
}
885812

886-
impl<'a> IntoVNode<'a> for &Option<VNode<'a>> {
887-
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
888-
self.as_ref()
889-
.map(|f| f.into_vnode(cx))
890-
.unwrap_or_else(|| cx.fragment_from_iter(None as Option<VNode>))
891-
}
892-
}
893-
894813
impl<'a> IntoVNode<'a> for &VNode<'a> {
895814
fn into_vnode(self, _cx: NodeFactory<'a>) -> VNode<'a> {
896815
// borrowed nodes are strange
897816
self.decouple()
898817
}
899818
}
819+
820+
// Note that we're using the E as a generic but this is never crafted anyways.
821+
pub struct FromNodeIterator;
822+
impl<'a, T, I, E> IntoVNode<'a, FromNodeIterator, E> for T
823+
where
824+
T: IntoIterator<Item = I>,
825+
I: IntoVNode<'a, E>,
826+
{
827+
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
828+
let mut nodes = bumpalo::collections::Vec::new_in(cx.bump);
829+
830+
for node in self {
831+
nodes.push(node.into_vnode(cx));
832+
}
833+
834+
if nodes.is_empty() {
835+
VNode::Placeholder(cx.bump.alloc(VPlaceholder { id: empty_cell() }))
836+
} else {
837+
let children = nodes.into_bump_slice();
838+
839+
if cfg!(debug_assertions)
840+
&& children.len() > 1
841+
&& children.last().unwrap().key().is_none()
842+
{
843+
// todo: make the backtrace prettier or remove it altogether
844+
log::error!(
845+
r#"
846+
Warning: Each child in an array or iterator should have a unique "key" prop.
847+
Not providing a key will lead to poor performance with lists.
848+
See docs.rs/dioxus for more information.
849+
-------------
850+
{:?}
851+
"#,
852+
backtrace::Backtrace::new()
853+
);
854+
}
855+
856+
VNode::Fragment(cx.bump.alloc(VFragment {
857+
children,
858+
key: None,
859+
}))
860+
}
861+
}
862+
}

packages/dioxus/tests/borrowedstate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fn test_borrowed_state() {
88
}
99

1010
fn Parent(cx: Scope) -> Element {
11-
let value = cx.use_hook(|| String::new());
11+
let value = cx.use_hook(String::new);
1212

1313
cx.render(rsx! {
1414
div {

packages/dioxus/tests/create_dom.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ fn create_components() {
157157
fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
158158
cx.render(rsx! {
159159
h1 {}
160-
div { {&cx.props.children} }
160+
div { &cx.props.children }
161161
p {}
162162
})
163163
}

0 commit comments

Comments
 (0)