Browse Source

wip: move children onto scope

Jonathan Kelley 3 years ago
parent
commit
f438bbcfd2

+ 1 - 1
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 }

+ 3 - 3
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;
 }

+ 62 - 0
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<VNode<'a>>,
+}
+
+impl IntoIterator for &ScopeChildren<'_> {
+    type Item = Self;
+
+    type IntoIter = std::iter::Once<Self>;
+
+    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<VNode>),
+        }
+    }
+}

+ 0 - 2
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,

+ 18 - 12
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();

+ 1 - 1
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;