diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 334ebcee1..8088b55d4 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -37,7 +37,7 @@ once_cell = "1.8.0" indexmap = "1.7.0" -# # Serialize the Edits for use in Webview/Liveview instances +# Serialize the Edits for use in Webview/Liveview instances serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1.7", optional = true } diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index cdc532ac1..8fc6bddc7 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -66,15 +66,15 @@ pub(crate) mod innerlude { pub use crate::innerlude::{ Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, MountType, Mutations, - NodeFactory, Properties, SchedulerMsg, ScopeId, SuspendedContext, TaskHandle, TestDom, - ThreadsafeVirtualDom, UserEvent, VNode, VirtualDom, FC, + NodeFactory, Properties, SchedulerMsg, ScopeChildren, ScopeId, SuspendedContext, TaskHandle, + TestDom, ThreadsafeVirtualDom, UserEvent, VNode, VirtualDom, FC, }; pub mod prelude { pub use crate::component::{fc_to_builder, Fragment, Properties, Scope}; pub use crate::context::Context; pub use crate::hooks::*; - pub use crate::innerlude::{DioxusElement, Element, LazyNodes, NodeFactory, FC}; + pub use crate::innerlude::{DioxusElement, Element, LazyNodes, NodeFactory, ScopeChildren, FC}; pub use crate::nodes::VNode; pub use crate::VirtualDom; } diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 217374666..69ca60ede 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -726,3 +726,65 @@ impl IntoVNode<'_> for Arguments<'_> { cx.text(self) } } + +/// Access the children elements passed into the component +/// +/// This enables patterns where a component is passed children from its parent. +/// +/// ## Details +/// +/// Unlike React, Dioxus allows *only* lists of children to be passed from parent to child - not arbitrary functions +/// or classes. If you want to generate nodes instead of accepting them as a list, consider declaring a closure +/// on the props that takes Context. +/// +/// If a parent passes children into a component, the child will always re-render when the parent re-renders. In other +/// words, a component cannot be automatically memoized if it borrows nodes from its parent, even if the component's +/// props are valid for the static lifetime. +/// +/// ## Example +/// +/// ```rust +/// const App: FC<()> = |(cx, props)|{ +/// cx.render(rsx!{ +/// CustomCard { +/// h1 {} +/// p {} +/// } +/// }) +/// } +/// +/// const CustomCard: FC<()> = |(cx, props)|{ +/// cx.render(rsx!{ +/// div { +/// h1 {"Title card"} +/// {props.children} +/// } +/// }) +/// } +/// ``` +/// +/// ## Notes: +/// +/// This method returns a "ScopeChildren" object. This object is copy-able and preserve the correct lifetime. +pub struct ScopeChildren<'a> { + root: Option>, +} + +impl IntoIterator for &ScopeChildren<'_> { + type Item = Self; + + type IntoIter = std::iter::Once; + + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} + +impl<'a> IntoVNode<'a> for &ScopeChildren<'a> { + fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> { + match &self.root { + Some(n) => n.decouple(), + None => cx.fragment_from_iter(None as Option), + } + } +} diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index ccfe64855..a2ce1d87c 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -340,8 +340,6 @@ impl ScopeInner { let render: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> = unsafe { &*self.caller }; // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders. - // - // todo!() if let Some(builder) = render(self) { let new_head = builder.into_vnode(NodeFactory { bump: &self.frames.wip_frame().bump, diff --git a/packages/core/tests/create_dom.rs b/packages/core/tests/create_dom.rs index dbfc56539..f40343362 100644 --- a/packages/core/tests/create_dom.rs +++ b/packages/core/tests/create_dom.rs @@ -64,10 +64,10 @@ fn create() { "Hello, world!" div { div { - Fragment { - "hello" - "world" - } + // Fragment { + // "hello" + // "world" + // } } } } @@ -208,20 +208,26 @@ fn create_simple() { #[test] fn create_components() { static App: FC<()> = |(cx, props)| { - cx.render(rsx! { - Child { "abc1" } - Child { "abc2" } - Child { "abc3" } - }) + todo!() + // cx.render(rsx! { + // Child { "abc1" } + // Child { "abc2" } + // Child { "abc3" } + // }) }; - static Child: FC<()> = |(cx, props)| { + #[derive(Props)] + struct ChildProps<'a> { + children: ScopeChildren<'a>, + } + + fn Child<'a>((cx, props): Scope<'a, ChildProps<'a>>) -> Element { cx.render(rsx! { h1 {} - div { {cx.children()} } + div { {&props.children} } p {} }) - }; + } let mut dom = new_dom(App, ()); let mutations = dom.rebuild(); diff --git a/packages/core/tests/sharedstate.rs b/packages/core/tests/sharedstate.rs index bad43442a..7bb9e6501 100644 --- a/packages/core/tests/sharedstate.rs +++ b/packages/core/tests/sharedstate.rs @@ -1,6 +1,6 @@ #![allow(unused, non_upper_case_globals)] -use dioxus::{prelude::*, DomEdit, TestDom}; +use dioxus::{prelude::*, DomEdit, Mutations, TestDom}; use dioxus_core as dioxus; use dioxus_core_macro::*; use dioxus_html as dioxus_elements;