1
0

nodes.rs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  1. //! Virtual Node Support
  2. //!
  3. //! VNodes represent lazily-constructed VDom trees that support diffing and event handlers. These VNodes should be *very*
  4. //! cheap and *very* fast to construct - building a full tree should be quick.
  5. use crate::{
  6. innerlude::{Context, Element, Properties, Scope, ScopeId},
  7. lazynodes::LazyNodes,
  8. };
  9. use bumpalo::{boxed::Box as BumpBox, Bump};
  10. use std::{
  11. any::Any,
  12. cell::{Cell, RefCell},
  13. fmt::{Arguments, Debug, Formatter},
  14. sync::Arc,
  15. };
  16. /// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
  17. ///
  18. /// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
  19. ///
  20. /// - the [`rsx`] macro
  21. /// - the [`NodeFactory`] API
  22. pub enum VNode<'src> {
  23. /// Text VNodes simply bump-allocated (or static) string slices
  24. ///
  25. /// # Example
  26. ///
  27. /// ```rust, ignore
  28. /// let mut vdom = VirtualDom::new();
  29. /// let node = vdom.render_vnode(rsx!( "hello" ));
  30. ///
  31. /// if let VNode::Text(vtext) = node {
  32. /// assert_eq!(vtext.text, "hello");
  33. /// assert_eq!(vtext.dom_id.get(), None);
  34. /// assert_eq!(vtext.is_static, true);
  35. /// }
  36. /// ```
  37. Text(&'src VText<'src>),
  38. /// Element VNodes are VNodes that may contain attributes, listeners, a key, a tag, and children.
  39. ///
  40. /// # Example
  41. ///
  42. /// ```rust, ignore
  43. /// let mut vdom = VirtualDom::new();
  44. ///
  45. /// let node = vdom.render_vnode(rsx!{
  46. /// div {
  47. /// key: "a",
  48. /// onclick: |e| log::info!("clicked"),
  49. /// hidden: "true",
  50. /// style: { background_color: "red" }
  51. /// "hello"
  52. /// }
  53. /// });
  54. ///
  55. /// if let VNode::Element(velement) = node {
  56. /// assert_eq!(velement.tag_name, "div");
  57. /// assert_eq!(velement.namespace, None);
  58. /// assert_eq!(velement.key, Some("a));
  59. /// }
  60. /// ```
  61. Element(&'src VElement<'src>),
  62. /// Fragment nodes may contain many VNodes without a single root.
  63. ///
  64. /// # Example
  65. ///
  66. /// ```rust, ignore
  67. /// rsx!{
  68. /// a {}
  69. /// link {}
  70. /// style {}
  71. /// "asd"
  72. /// Example {}
  73. /// }
  74. /// ```
  75. Fragment(VFragment<'src>),
  76. /// Component nodes represent a mounted component with props, children, and a key.
  77. ///
  78. /// # Example
  79. ///
  80. /// ```rust, ignore
  81. /// fn Example(cx: Context, props: &()) -> Element {
  82. /// todo!()
  83. /// }
  84. ///
  85. /// let mut vdom = VirtualDom::new();
  86. ///
  87. /// let node = vdom.render_vnode(rsx!( Example {} ));
  88. ///
  89. /// if let VNode::Component(vcomp) = node {
  90. /// assert_eq!(vcomp.user_fc, Example as *const ());
  91. /// }
  92. /// ```
  93. Component(&'src VComponent<'src>),
  94. /// Suspended VNodes represent chunks of the UI tree that are not yet ready to be displayed.
  95. ///
  96. /// # Example
  97. ///
  98. /// ```rust, ignore
  99. ///
  100. ///
  101. /// ```
  102. Suspended(&'src VSuspended),
  103. /// Anchors are a type of placeholder VNode used when fragments don't contain any children.
  104. ///
  105. /// Anchors cannot be directly constructed via public APIs.
  106. ///
  107. /// # Example
  108. ///
  109. /// ```rust, ignore
  110. /// let mut vdom = VirtualDom::new();
  111. ///
  112. /// let node = vdom.render_vnode(rsx!( Fragment {} ));
  113. ///
  114. /// if let VNode::Fragment(frag) = node {
  115. /// let root = &frag.children[0];
  116. /// assert_eq!(root, VNode::Anchor);
  117. /// }
  118. /// ```
  119. Anchor(&'src VAnchor),
  120. /// A VNode that is actually a pointer to some nodes rather than the nodes directly. Useful when rendering portals
  121. /// or eliding lifetimes on VNodes through runtime checks.
  122. ///
  123. /// Linked VNodes can only be made through the [`Context::render`] method
  124. ///
  125. /// Typically, linked nodes are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
  126. /// an `rsx!` call.
  127. ///
  128. /// # Example
  129. /// ```rust, ignore
  130. /// let mut vdom = VirtualDom::new();
  131. ///
  132. /// let node: NodeLink = vdom.render_vnode(rsx!( "hello" ));
  133. /// ```
  134. Linked(NodeLink),
  135. }
  136. impl<'src> VNode<'src> {
  137. /// Get the VNode's "key" used in the keyed diffing algorithm.
  138. pub fn key(&self) -> Option<&'src str> {
  139. match &self {
  140. VNode::Element(el) => el.key,
  141. VNode::Component(c) => c.key,
  142. VNode::Fragment(f) => f.key,
  143. VNode::Text(_t) => None,
  144. VNode::Suspended(_s) => None,
  145. VNode::Anchor(_f) => None,
  146. VNode::Linked(_c) => None,
  147. }
  148. }
  149. /// Get the ElementID of the mounted VNode.
  150. ///
  151. /// Panics if the mounted ID is None or if the VNode is not represented by a single Element.
  152. pub fn mounted_id(&self) -> ElementId {
  153. self.try_mounted_id().unwrap()
  154. }
  155. /// Try to get the ElementID of the mounted VNode.
  156. ///
  157. /// Returns None if the VNode is not mounted, or if the VNode cannot be presented by a mounted ID (Fragment/Component)
  158. pub fn try_mounted_id(&self) -> Option<ElementId> {
  159. match &self {
  160. VNode::Text(el) => el.dom_id.get(),
  161. VNode::Element(el) => el.dom_id.get(),
  162. VNode::Anchor(el) => el.dom_id.get(),
  163. VNode::Suspended(el) => el.dom_id.get(),
  164. VNode::Linked(_) => None,
  165. VNode::Fragment(_) => None,
  166. VNode::Component(_) => None,
  167. }
  168. }
  169. pub(crate) fn children(&self) -> &[VNode<'src>] {
  170. match &self {
  171. VNode::Fragment(f) => f.children,
  172. VNode::Component(_c) => todo!("children are not accessible through this"),
  173. _ => &[],
  174. }
  175. }
  176. // Create an "owned" version of the vnode.
  177. pub fn decouple(&self) -> VNode<'src> {
  178. match self {
  179. VNode::Text(t) => VNode::Text(*t),
  180. VNode::Element(e) => VNode::Element(*e),
  181. VNode::Component(c) => VNode::Component(*c),
  182. VNode::Suspended(s) => VNode::Suspended(*s),
  183. VNode::Anchor(a) => VNode::Anchor(*a),
  184. VNode::Fragment(f) => VNode::Fragment(VFragment {
  185. children: f.children,
  186. key: f.key,
  187. }),
  188. VNode::Linked(c) => VNode::Linked(NodeLink {
  189. scope_id: c.scope_id.clone(),
  190. link_idx: c.link_idx.clone(),
  191. node: c.node,
  192. }),
  193. }
  194. }
  195. }
  196. impl Debug for VNode<'_> {
  197. fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
  198. match &self {
  199. VNode::Element(el) => s
  200. .debug_struct("VNode::VElement")
  201. .field("name", &el.tag_name)
  202. .field("key", &el.key)
  203. .finish(),
  204. VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text),
  205. VNode::Anchor(_) => write!(s, "VNode::VAnchor"),
  206. VNode::Fragment(frag) => {
  207. write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
  208. }
  209. VNode::Suspended { .. } => write!(s, "VNode::VSuspended"),
  210. VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
  211. VNode::Linked(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
  212. }
  213. }
  214. }
  215. /// An Element's unique identifier.
  216. ///
  217. /// `ElementId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is
  218. /// unmounted, then the `ElementId` will be reused for a new component.
  219. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
  220. pub struct ElementId(pub usize);
  221. impl std::fmt::Display for ElementId {
  222. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  223. write!(f, "{}", self.0)
  224. }
  225. }
  226. impl ElementId {
  227. pub fn as_u64(self) -> u64 {
  228. self.0 as u64
  229. }
  230. }
  231. fn empty_cell() -> Cell<Option<ElementId>> {
  232. Cell::new(None)
  233. }
  234. /// A placeholder node only generated when Fragments don't have any children.
  235. pub struct VAnchor {
  236. pub dom_id: Cell<Option<ElementId>>,
  237. }
  238. /// A bump-allocated string slice and metadata.
  239. pub struct VText<'src> {
  240. pub text: &'src str,
  241. pub dom_id: Cell<Option<ElementId>>,
  242. pub is_static: bool,
  243. }
  244. /// A list of VNodes with no single root.
  245. pub struct VFragment<'src> {
  246. pub key: Option<&'src str>,
  247. pub children: &'src [VNode<'src>],
  248. }
  249. /// An element like a "div" with children, listeners, and attributes.
  250. pub struct VElement<'a> {
  251. pub tag_name: &'static str,
  252. pub namespace: Option<&'static str>,
  253. pub key: Option<&'a str>,
  254. pub dom_id: Cell<Option<ElementId>>,
  255. pub parent_id: Cell<Option<ElementId>>,
  256. pub listeners: &'a [Listener<'a>],
  257. pub attributes: &'a [Attribute<'a>],
  258. pub children: &'a [VNode<'a>],
  259. }
  260. impl Debug for VElement<'_> {
  261. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
  262. f.debug_struct("VElement")
  263. .field("tag_name", &self.tag_name)
  264. .field("namespace", &self.namespace)
  265. .field("key", &self.key)
  266. .field("dom_id", &self.dom_id)
  267. .field("parent_id", &self.parent_id)
  268. .field("listeners", &self.listeners.len())
  269. .field("attributes", &self.attributes)
  270. .field("children", &self.children)
  271. .finish()
  272. }
  273. }
  274. /// A trait for any generic Dioxus Element.
  275. ///
  276. /// This trait provides the ability to use custom elements in the `rsx!` macro.
  277. ///
  278. /// ```rust, ignore
  279. /// struct my_element;
  280. ///
  281. /// impl DioxusElement for my_element {
  282. /// const TAG_NAME: "my_element";
  283. /// const NAME_SPACE: None;
  284. /// }
  285. ///
  286. /// let _ = rsx!{
  287. /// my_element {}
  288. /// };
  289. /// ```
  290. pub trait DioxusElement {
  291. const TAG_NAME: &'static str;
  292. const NAME_SPACE: Option<&'static str>;
  293. #[inline]
  294. fn tag_name(&self) -> &'static str {
  295. Self::TAG_NAME
  296. }
  297. #[inline]
  298. fn namespace(&self) -> Option<&'static str> {
  299. Self::NAME_SPACE
  300. }
  301. }
  302. /// An attribute on a DOM node, such as `id="my-thing"` or
  303. /// `href="https://example.com"`.
  304. #[derive(Clone, Debug)]
  305. pub struct Attribute<'a> {
  306. pub name: &'static str,
  307. pub value: &'a str,
  308. pub is_static: bool,
  309. pub is_volatile: bool,
  310. // Doesn't exist in the html spec.
  311. // Used in Dioxus to denote "style" tags.
  312. pub namespace: Option<&'static str>,
  313. }
  314. /// An event listener.
  315. /// IE onclick, onkeydown, etc
  316. pub struct Listener<'bump> {
  317. /// The ID of the node that this listener is mounted to
  318. /// Used to generate the event listener's ID on the DOM
  319. pub mounted_node: Cell<Option<ElementId>>,
  320. /// The type of event to listen for.
  321. ///
  322. /// IE "click" - whatever the renderer needs to attach the listener by name.
  323. pub event: &'static str,
  324. /// The actual callback that the user specified
  325. pub(crate) callback:
  326. RefCell<Option<BumpBox<'bump, dyn FnMut(std::sync::Arc<dyn Any + Send + Sync>) + 'bump>>>,
  327. }
  328. /// Virtual Components for custom user-defined components
  329. /// Only supports the functional syntax
  330. pub struct VComponent<'src> {
  331. pub key: Option<&'src str>,
  332. pub associated_scope: Cell<Option<ScopeId>>,
  333. // Function pointer to the FC that was used to generate this component
  334. pub user_fc: *const (),
  335. pub(crate) can_memoize: bool,
  336. pub(crate) _hard_allocation: Cell<Option<*const ()>>,
  337. // Raw pointer into the bump arena for the props of the component
  338. pub(crate) bump_props: *const (),
  339. // during the "teardown" process we'll take the caller out so it can be dropped properly
  340. // pub(crate) caller: Option<VCompCaller<'src>>,
  341. pub(crate) caller: &'src dyn Fn(&'src Scope) -> Element,
  342. pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
  343. pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
  344. }
  345. pub struct VSuspended {
  346. pub task_id: usize,
  347. pub scope: Cell<Option<ScopeId>>,
  348. pub dom_id: Cell<Option<ElementId>>,
  349. pub parent: Cell<Option<ElementId>>,
  350. }
  351. /// A cached node is a "pointer" to a "rendered" node in a particular scope
  352. ///
  353. /// It does not provide direct access to the node, so it doesn't carry any lifetime information with it
  354. ///
  355. /// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
  356. /// is essentially a unique key to guarantee safe usage of the Node.
  357. ///
  358. /// Linked VNodes can only be made through the [`Context::render`] method
  359. ///
  360. /// Typically, NodeLinks are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
  361. /// an `rsx!` call.
  362. #[derive(Debug)]
  363. pub struct NodeLink {
  364. pub(crate) link_idx: Cell<usize>,
  365. pub(crate) scope_id: Cell<Option<ScopeId>>,
  366. pub(crate) node: *const VNode<'static>,
  367. }
  368. impl PartialEq for NodeLink {
  369. fn eq(&self, other: &Self) -> bool {
  370. self.node == other.node
  371. }
  372. }
  373. /// This struct provides an ergonomic API to quickly build VNodes.
  374. ///
  375. /// NodeFactory is used to build VNodes in the component's memory space.
  376. /// This struct adds metadata to the final VNode about listeners, attributes, and children
  377. #[derive(Copy, Clone)]
  378. pub struct NodeFactory<'a> {
  379. pub(crate) bump: &'a Bump,
  380. }
  381. impl<'a> NodeFactory<'a> {
  382. pub fn new(bump: &'a Bump) -> NodeFactory<'a> {
  383. NodeFactory { bump }
  384. }
  385. #[inline]
  386. pub fn bump(&self) -> &'a bumpalo::Bump {
  387. self.bump
  388. }
  389. /// Directly pass in text blocks without the need to use the format_args macro.
  390. pub fn static_text(&self, text: &'static str) -> VNode<'a> {
  391. VNode::Text(self.bump.alloc(VText {
  392. dom_id: empty_cell(),
  393. text,
  394. is_static: true,
  395. }))
  396. }
  397. /// Parses a lazy text Arguments and returns a string and a flag indicating if the text is 'static
  398. ///
  399. /// Text that's static may be pointer compared, making it cheaper to diff
  400. pub fn raw_text(&self, args: Arguments) -> (&'a str, bool) {
  401. match args.as_str() {
  402. Some(static_str) => (static_str, true),
  403. None => {
  404. use bumpalo::core_alloc::fmt::Write;
  405. let mut str_buf = bumpalo::collections::String::new_in(self.bump);
  406. str_buf.write_fmt(args).unwrap();
  407. (str_buf.into_bump_str(), false)
  408. }
  409. }
  410. }
  411. /// Create some text that's allocated along with the other vnodes
  412. ///
  413. pub fn text(&self, args: Arguments) -> VNode<'a> {
  414. let (text, is_static) = self.raw_text(args);
  415. VNode::Text(self.bump.alloc(VText {
  416. text,
  417. is_static,
  418. dom_id: empty_cell(),
  419. }))
  420. }
  421. pub fn element<L, A, V>(
  422. &self,
  423. el: impl DioxusElement,
  424. listeners: L,
  425. attributes: A,
  426. children: V,
  427. key: Option<Arguments>,
  428. ) -> VNode<'a>
  429. where
  430. L: 'a + AsRef<[Listener<'a>]>,
  431. A: 'a + AsRef<[Attribute<'a>]>,
  432. V: 'a + AsRef<[VNode<'a>]>,
  433. {
  434. self.raw_element(
  435. el.tag_name(),
  436. el.namespace(),
  437. listeners,
  438. attributes,
  439. children,
  440. key,
  441. )
  442. }
  443. pub fn raw_element<L, A, V>(
  444. &self,
  445. tag_name: &'static str,
  446. namespace: Option<&'static str>,
  447. listeners: L,
  448. attributes: A,
  449. children: V,
  450. key: Option<Arguments>,
  451. ) -> VNode<'a>
  452. where
  453. L: 'a + AsRef<[Listener<'a>]>,
  454. A: 'a + AsRef<[Attribute<'a>]>,
  455. V: 'a + AsRef<[VNode<'a>]>,
  456. {
  457. let listeners: &'a L = self.bump.alloc(listeners);
  458. let listeners = listeners.as_ref();
  459. let attributes: &'a A = self.bump.alloc(attributes);
  460. let attributes = attributes.as_ref();
  461. let children: &'a V = self.bump.alloc(children);
  462. let children = children.as_ref();
  463. let key = key.map(|f| self.raw_text(f).0);
  464. VNode::Element(self.bump.alloc(VElement {
  465. tag_name,
  466. key,
  467. namespace,
  468. listeners,
  469. attributes,
  470. children,
  471. dom_id: empty_cell(),
  472. parent_id: empty_cell(),
  473. }))
  474. }
  475. pub fn attr(
  476. &self,
  477. name: &'static str,
  478. val: Arguments,
  479. namespace: Option<&'static str>,
  480. is_volatile: bool,
  481. ) -> Attribute<'a> {
  482. let (value, is_static) = self.raw_text(val);
  483. Attribute {
  484. name,
  485. value,
  486. is_static,
  487. namespace,
  488. is_volatile,
  489. }
  490. }
  491. pub fn component<P>(
  492. &self,
  493. component: fn(Context<'a>, &'a P) -> Element,
  494. props: P,
  495. key: Option<Arguments>,
  496. ) -> VNode<'a>
  497. where
  498. P: Properties + 'a,
  499. {
  500. let bump = self.bump;
  501. let props = bump.alloc(props);
  502. let bump_props = props as *mut P as *mut ();
  503. let user_fc = component as *const ();
  504. let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
  505. move |other: &VComponent| {
  506. if user_fc == other.user_fc {
  507. // Safety
  508. // - We guarantee that FC<P> is the same by function pointer
  509. // - Because FC<P> is the same, then P must be the same (even with generics)
  510. // - Non-static P are autoderived to memoize as false
  511. // - This comparator is only called on a corresponding set of bumpframes
  512. //
  513. // It's only okay to memoize if there are no children and the props can be memoized
  514. // Implementing memoize is unsafe and done automatically with the props trait
  515. unsafe {
  516. let real_other: &P = &*(other.bump_props as *const _ as *const P);
  517. props.memoize(real_other)
  518. }
  519. } else {
  520. false
  521. }
  522. }
  523. });
  524. let drop_props = {
  525. // create a closure to drop the props
  526. let mut has_dropped = false;
  527. let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
  528. move || unsafe {
  529. if !has_dropped {
  530. let real_other = bump_props as *mut _ as *mut P;
  531. let b = BumpBox::from_raw(real_other);
  532. std::mem::drop(b);
  533. has_dropped = true;
  534. } else {
  535. panic!("Drop props called twice - this is an internal failure of Dioxus");
  536. }
  537. }
  538. });
  539. let drop_props = unsafe { BumpBox::from_raw(drop_props) };
  540. RefCell::new(Some(drop_props))
  541. };
  542. let key = key.map(|f| self.raw_text(f).0);
  543. let caller: &'a mut dyn Fn(&'a Scope) -> Element =
  544. bump.alloc(move |scope: &Scope| -> Element {
  545. let props: &'_ P = unsafe { &*(bump_props as *const P) };
  546. component(scope, props)
  547. });
  548. let can_memoize = P::IS_STATIC;
  549. VNode::Component(bump.alloc(VComponent {
  550. user_fc,
  551. comparator: Some(comparator),
  552. bump_props,
  553. caller,
  554. key,
  555. can_memoize,
  556. drop_props,
  557. associated_scope: Cell::new(None),
  558. _hard_allocation: Cell::new(None),
  559. }))
  560. }
  561. pub fn listener(
  562. self,
  563. event: &'static str,
  564. callback: BumpBox<'a, dyn FnMut(Arc<dyn Any + Send + Sync>) + 'a>,
  565. ) -> Listener<'a> {
  566. Listener {
  567. mounted_node: Cell::new(None),
  568. event,
  569. callback: RefCell::new(Some(callback)),
  570. }
  571. }
  572. pub fn fragment_from_iter<'b, 'c>(
  573. self,
  574. node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
  575. ) -> VNode<'a> {
  576. let bump = self.bump;
  577. let mut nodes = bumpalo::collections::Vec::new_in(bump);
  578. for node in node_iter {
  579. nodes.push(node.into_vnode(self));
  580. }
  581. if nodes.is_empty() {
  582. nodes.push(VNode::Anchor(bump.alloc(VAnchor {
  583. dom_id: empty_cell(),
  584. })));
  585. }
  586. let children = nodes.into_bump_slice();
  587. // todo: add a backtrace
  588. if cfg!(debug_assertions) && children.len() > 1 && children.last().unwrap().key().is_none()
  589. {
  590. // todo: rsx! calls get turned into fragments which means they always trip this error
  591. //
  592. //
  593. // use backtrace::Backtrace;
  594. // let bt = Backtrace::new();
  595. log::error!(
  596. r#"
  597. Warning: Each child in an array or iterator should have a unique "key" prop.
  598. Not providing a key will lead to poor performance with lists.
  599. See docs.rs/dioxus for more information.
  600. -------------
  601. "#,
  602. // bt
  603. );
  604. }
  605. VNode::Fragment(VFragment {
  606. children,
  607. key: None,
  608. })
  609. }
  610. // this isn't quite feasible yet
  611. // I think we need some form of interior mutability or state on nodefactory that stores which subtree was created
  612. pub fn create_children(
  613. self,
  614. node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
  615. ) -> Element {
  616. let bump = self.bump;
  617. let mut nodes = bumpalo::collections::Vec::new_in(bump);
  618. for node in node_iter {
  619. nodes.push(node.into_vnode(self));
  620. }
  621. if nodes.is_empty() {
  622. nodes.push(VNode::Anchor(bump.alloc(VAnchor {
  623. dom_id: empty_cell(),
  624. })));
  625. }
  626. let children = nodes.into_bump_slice();
  627. // TODO
  628. // We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
  629. //
  630. // if cfg!(debug_assertions) {
  631. // if children.len() > 1 {
  632. // if children.last().unwrap().key().is_none() {
  633. // log::error!(
  634. // r#"
  635. // Warning: Each child in an array or iterator should have a unique "key" prop.
  636. // Not providing a key will lead to poor performance with lists.
  637. // See docs.rs/dioxus for more information.
  638. // ---
  639. // To help you identify where this error is coming from, we've generated a backtrace.
  640. // "#,
  641. // );
  642. // }
  643. // }
  644. // }
  645. let frag = VNode::Fragment(VFragment {
  646. children,
  647. key: None,
  648. });
  649. let ptr = self.bump.alloc(frag) as *const _;
  650. Some(NodeLink {
  651. link_idx: Default::default(),
  652. scope_id: Default::default(),
  653. node: unsafe { std::mem::transmute(ptr) },
  654. })
  655. }
  656. pub fn annotate_lazy<'z, 'b>(
  657. f: impl FnOnce(NodeFactory<'z>) -> VNode<'z> + 'b,
  658. ) -> Option<LazyNodes<'z, 'b>> {
  659. Some(LazyNodes::new(f))
  660. }
  661. }
  662. impl Debug for NodeFactory<'_> {
  663. fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  664. Ok(())
  665. }
  666. }
  667. /// Trait implementations for use in the rsx! and html! macros.
  668. ///
  669. /// ## Details
  670. ///
  671. /// This section provides convenience methods and trait implementations for converting common structs into a format accepted
  672. /// by the macros.
  673. ///
  674. /// All dynamic content in the macros must flow in through `fragment_from_iter`. Everything else must be statically layed out.
  675. /// We pipe basically everything through `fragment_from_iter`, so we expect a very specific type:
  676. /// ```rust, ignore
  677. /// impl IntoIterator<Item = impl IntoVNode<'a>>
  678. /// ```
  679. ///
  680. /// As such, all node creation must go through the factory, which is only available in the component context.
  681. /// These strict requirements make it possible to manage lifetimes and state.
  682. pub trait IntoVNode<'a> {
  683. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
  684. }
  685. // For the case where a rendered VNode is passed into the rsx! macro through curly braces
  686. impl<'a> IntoIterator for VNode<'a> {
  687. type Item = VNode<'a>;
  688. type IntoIter = std::iter::Once<Self::Item>;
  689. fn into_iter(self) -> Self::IntoIter {
  690. std::iter::once(self)
  691. }
  692. }
  693. // TODO: do we even need this? It almost seems better not to
  694. // // For the case where a rendered VNode is passed into the rsx! macro through curly braces
  695. impl<'a> IntoVNode<'a> for VNode<'a> {
  696. fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
  697. self
  698. }
  699. }
  700. // Conveniently, we also support "null" (nothing) passed in
  701. impl IntoVNode<'_> for () {
  702. fn into_vnode(self, cx: NodeFactory) -> VNode {
  703. cx.fragment_from_iter(None as Option<VNode>)
  704. }
  705. }
  706. // Conveniently, we also support "None"
  707. impl IntoVNode<'_> for Option<()> {
  708. fn into_vnode(self, cx: NodeFactory) -> VNode {
  709. cx.fragment_from_iter(None as Option<VNode>)
  710. }
  711. }
  712. impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
  713. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  714. self.unwrap_or_else(|| cx.fragment_from_iter(None as Option<VNode>))
  715. }
  716. }
  717. impl<'a> IntoVNode<'a> for Option<LazyNodes<'a, '_>> {
  718. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  719. match self {
  720. Some(lazy) => lazy.call(cx),
  721. None => VNode::Fragment(VFragment {
  722. children: &[],
  723. key: None,
  724. }),
  725. }
  726. }
  727. }
  728. impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> {
  729. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  730. self.call(cx)
  731. }
  732. }
  733. impl IntoVNode<'_> for &'static str {
  734. fn into_vnode(self, cx: NodeFactory) -> VNode {
  735. cx.static_text(self)
  736. }
  737. }
  738. impl IntoVNode<'_> for Arguments<'_> {
  739. fn into_vnode(self, cx: NodeFactory) -> VNode {
  740. cx.text(self)
  741. }
  742. }
  743. // called cx.render from a helper function
  744. impl IntoVNode<'_> for Option<NodeLink> {
  745. fn into_vnode(self, _cx: NodeFactory) -> VNode {
  746. match self {
  747. Some(node) => VNode::Linked(node),
  748. None => {
  749. todo!()
  750. }
  751. }
  752. }
  753. }
  754. // essentially passing elements through props
  755. // just build a new element in place
  756. impl IntoVNode<'_> for &Option<NodeLink> {
  757. fn into_vnode(self, _cx: NodeFactory) -> VNode {
  758. match self {
  759. Some(node) => VNode::Linked(NodeLink {
  760. link_idx: node.link_idx.clone(),
  761. scope_id: node.scope_id.clone(),
  762. node: node.node,
  763. }),
  764. None => {
  765. //
  766. todo!()
  767. }
  768. }
  769. }
  770. }
  771. impl IntoVNode<'_> for NodeLink {
  772. fn into_vnode(self, _cx: NodeFactory) -> VNode {
  773. VNode::Linked(self)
  774. }
  775. }
  776. impl IntoVNode<'_> for &NodeLink {
  777. fn into_vnode(self, _cx: NodeFactory) -> VNode {
  778. VNode::Linked(NodeLink {
  779. link_idx: self.link_idx.clone(),
  780. scope_id: self.scope_id.clone(),
  781. node: self.node,
  782. })
  783. }
  784. }