mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-17 06:08:26 +00:00
feat: implement type magic to allow strings, format args, and other types directly in rsx (#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
This commit is contained in:
parent
38e8745db9
commit
67dc6e6017
7 changed files with 80 additions and 100 deletions
|
@ -203,8 +203,8 @@ fn app(cx: Scope) -> Element {
|
|||
self::lowercase_helper {}
|
||||
|
||||
// helper functions
|
||||
// Single values must be wrapped in braces or `Some` to satisfy `IntoIterator`
|
||||
[helper(&cx, "hello world!")]
|
||||
// Anything that implements IntoVnode can be dropped directly into Rsx
|
||||
helper(&cx, "hello world!")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
19
examples/simple_list.rs
Normal file
19
examples/simple_list.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx!(
|
||||
// Use Map directly to lazily pull elements
|
||||
(0..10).map(|f| rsx! { "{f}" }),
|
||||
// Collect into an intermediate collection if necessary
|
||||
["a", "b", "c"]
|
||||
.into_iter()
|
||||
.map(|f| rsx! { "{f}" })
|
||||
.collect::<Vec<_>>(),
|
||||
// Use optionals
|
||||
Some(rsx! { "Some" }),
|
||||
))
|
||||
}
|
|
@ -16,7 +16,7 @@ mod util;
|
|||
///
|
||||
/// Note that this is tailored to VSCode's TextEdit API and not a general Diff API. Line numbers are not accurate if
|
||||
/// multiple edits are applied in a single file without tracking text shifts.
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq, Hash)]
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct FormattedBlock {
|
||||
/// The new contents of the block
|
||||
pub formatted: String,
|
||||
|
|
|
@ -288,16 +288,14 @@ mod tests {
|
|||
}
|
||||
|
||||
let caller = {
|
||||
let it = (0..10)
|
||||
.map(|i| {
|
||||
let val = cx.props.inner.clone();
|
||||
LazyNodes::new(move |f| {
|
||||
eprintln!("hell closure");
|
||||
let inner = DropInner { id: i };
|
||||
f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
|
||||
})
|
||||
let it = (0..10).map(|i| {
|
||||
let val = cx.props.inner.clone();
|
||||
LazyNodes::new(move |f| {
|
||||
eprintln!("hell closure");
|
||||
let inner = DropInner { id: i };
|
||||
f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
});
|
||||
|
||||
LazyNodes::new(|f| {
|
||||
eprintln!("main closure");
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
//!
|
||||
//! VNodes represent lazily-constructed VDom trees that support diffing and event handlers. These VNodes should be *very*
|
||||
//! cheap and *very* fast to construct - building a full tree should be quick.
|
||||
|
||||
use crate::{
|
||||
innerlude::{AttributeValue, ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState},
|
||||
lazynodes::LazyNodes,
|
||||
|
@ -375,7 +374,6 @@ pub struct Listener<'bump> {
|
|||
|
||||
pub type InternalHandler<'bump> = &'bump RefCell<Option<InternalListenerCallback<'bump>>>;
|
||||
type InternalListenerCallback<'bump> = BumpBox<'bump, dyn FnMut(AnyEvent) + 'bump>;
|
||||
|
||||
type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
|
||||
|
||||
/// The callback type generated by the `rsx!` macro when an `on` field is specified for components.
|
||||
|
@ -706,43 +704,11 @@ impl<'a> NodeFactory<'a> {
|
|||
}
|
||||
|
||||
/// Create a new [`VNode::Fragment`] from any iterator
|
||||
pub fn fragment_from_iter<'b, 'c>(
|
||||
pub fn fragment_from_iter<'c, I, J>(
|
||||
self,
|
||||
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
|
||||
node_iter: impl IntoVNode<'a, I, J> + 'c,
|
||||
) -> VNode<'a> {
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
|
||||
|
||||
for node in node_iter {
|
||||
nodes.push(node.into_vnode(self));
|
||||
}
|
||||
|
||||
if nodes.is_empty() {
|
||||
VNode::Placeholder(self.bump.alloc(VPlaceholder { id: empty_cell() }))
|
||||
} else {
|
||||
let children = nodes.into_bump_slice();
|
||||
|
||||
if cfg!(debug_assertions)
|
||||
&& children.len() > 1
|
||||
&& children.last().unwrap().key().is_none()
|
||||
{
|
||||
// todo: make the backtrace prettier or remove it altogether
|
||||
log::error!(
|
||||
r#"
|
||||
Warning: Each child in an array or iterator should have a unique "key" prop.
|
||||
Not providing a key will lead to poor performance with lists.
|
||||
See docs.rs/dioxus for more information.
|
||||
-------------
|
||||
{:?}
|
||||
"#,
|
||||
backtrace::Backtrace::new()
|
||||
);
|
||||
}
|
||||
|
||||
VNode::Fragment(self.bump.alloc(VFragment {
|
||||
children,
|
||||
key: None,
|
||||
}))
|
||||
}
|
||||
node_iter.into_vnode(self)
|
||||
}
|
||||
|
||||
/// Create a new [`VNode`] from any iterator of children
|
||||
|
@ -800,20 +766,11 @@ impl Debug for NodeFactory<'_> {
|
|||
///
|
||||
/// As such, all node creation must go through the factory, which is only available in the component context.
|
||||
/// These strict requirements make it possible to manage lifetimes and state.
|
||||
pub trait IntoVNode<'a> {
|
||||
pub trait IntoVNode<'a, I = (), J = ()> {
|
||||
/// Convert this into a [`VNode`], using the [`NodeFactory`] as a source of allocation
|
||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
|
||||
}
|
||||
|
||||
// For the case where a rendered VNode is passed into the rsx! macro through curly braces
|
||||
impl<'a> IntoIterator for VNode<'a> {
|
||||
type Item = VNode<'a>;
|
||||
type IntoIter = std::iter::Once<Self::Item>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
std::iter::once(self)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: do we even need this? It almost seems better not to
|
||||
// // For the case where a rendered VNode is passed into the rsx! macro through curly braces
|
||||
impl<'a> IntoVNode<'a> for VNode<'a> {
|
||||
|
@ -825,37 +782,7 @@ impl<'a> IntoVNode<'a> for VNode<'a> {
|
|||
// Conveniently, we also support "null" (nothing) passed in
|
||||
impl IntoVNode<'_> for () {
|
||||
fn into_vnode(self, cx: NodeFactory) -> VNode {
|
||||
cx.fragment_from_iter(None as Option<VNode>)
|
||||
}
|
||||
}
|
||||
|
||||
// Conveniently, we also support "None"
|
||||
impl IntoVNode<'_> for Option<()> {
|
||||
fn into_vnode(self, cx: NodeFactory) -> VNode {
|
||||
cx.fragment_from_iter(None as Option<VNode>)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
|
||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||
self.unwrap_or_else(|| cx.fragment_from_iter(None as Option<VNode>))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoVNode<'a> for Option<LazyNodes<'a, '_>> {
|
||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||
match self {
|
||||
Some(lazy) => lazy.call(cx),
|
||||
None => VNode::Placeholder(cx.bump.alloc(VPlaceholder { id: empty_cell() })),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoIterator for LazyNodes<'a, 'b> {
|
||||
type Item = LazyNodes<'a, 'b>;
|
||||
type IntoIter = std::iter::Once<Self::Item>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
std::iter::once(self)
|
||||
VNode::Placeholder(cx.bump.alloc(VPlaceholder { id: empty_cell() }))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -883,17 +810,53 @@ impl IntoVNode<'_> for Arguments<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoVNode<'a> for &Option<VNode<'a>> {
|
||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||
self.as_ref()
|
||||
.map(|f| f.into_vnode(cx))
|
||||
.unwrap_or_else(|| cx.fragment_from_iter(None as Option<VNode>))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoVNode<'a> for &VNode<'a> {
|
||||
fn into_vnode(self, _cx: NodeFactory<'a>) -> VNode<'a> {
|
||||
// borrowed nodes are strange
|
||||
self.decouple()
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we're using the E as a generic but this is never crafted anyways.
|
||||
pub struct FromNodeIterator;
|
||||
impl<'a, T, I, E> IntoVNode<'a, FromNodeIterator, E> for T
|
||||
where
|
||||
T: IntoIterator<Item = I>,
|
||||
I: IntoVNode<'a, E>,
|
||||
{
|
||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(cx.bump);
|
||||
|
||||
for node in self {
|
||||
nodes.push(node.into_vnode(cx));
|
||||
}
|
||||
|
||||
if nodes.is_empty() {
|
||||
VNode::Placeholder(cx.bump.alloc(VPlaceholder { id: empty_cell() }))
|
||||
} else {
|
||||
let children = nodes.into_bump_slice();
|
||||
|
||||
if cfg!(debug_assertions)
|
||||
&& children.len() > 1
|
||||
&& children.last().unwrap().key().is_none()
|
||||
{
|
||||
// todo: make the backtrace prettier or remove it altogether
|
||||
log::error!(
|
||||
r#"
|
||||
Warning: Each child in an array or iterator should have a unique "key" prop.
|
||||
Not providing a key will lead to poor performance with lists.
|
||||
See docs.rs/dioxus for more information.
|
||||
-------------
|
||||
{:?}
|
||||
"#,
|
||||
backtrace::Backtrace::new()
|
||||
);
|
||||
}
|
||||
|
||||
VNode::Fragment(cx.bump.alloc(VFragment {
|
||||
children,
|
||||
key: None,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ fn test_borrowed_state() {
|
|||
}
|
||||
|
||||
fn Parent(cx: Scope) -> Element {
|
||||
let value = cx.use_hook(|| String::new());
|
||||
let value = cx.use_hook(String::new);
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
|
|
@ -157,7 +157,7 @@ fn create_components() {
|
|||
fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
|
||||
cx.render(rsx! {
|
||||
h1 {}
|
||||
div { {&cx.props.children} }
|
||||
div { &cx.props.children }
|
||||
p {}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue