properties.rs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. use crate::innerlude::*;
  2. pub struct FragmentProps<'a>(Element<'a>);
  3. pub struct FragmentBuilder<'a, const BUILT: bool>(Element<'a>);
  4. impl<'a> FragmentBuilder<'a, false> {
  5. pub fn children(self, children: Element<'a>) -> FragmentBuilder<'a, true> {
  6. FragmentBuilder(children)
  7. }
  8. }
  9. impl<'a, const A: bool> FragmentBuilder<'a, A> {
  10. pub fn build(self) -> FragmentProps<'a> {
  11. FragmentProps(self.0)
  12. }
  13. }
  14. /// Access the children elements passed into the component
  15. ///
  16. /// This enables patterns where a component is passed children from its parent.
  17. ///
  18. /// ## Details
  19. ///
  20. /// Unlike React, Dioxus allows *only* lists of children to be passed from parent to child - not arbitrary functions
  21. /// or classes. If you want to generate nodes instead of accepting them as a list, consider declaring a closure
  22. /// on the props that takes Context.
  23. ///
  24. /// If a parent passes children into a component, the child will always re-render when the parent re-renders. In other
  25. /// words, a component cannot be automatically memoized if it borrows nodes from its parent, even if the component's
  26. /// props are valid for the static lifetime.
  27. ///
  28. /// ## Example
  29. ///
  30. /// ```rust, ignore
  31. /// fn App(cx: Scope) -> Element {
  32. /// cx.render(rsx!{
  33. /// CustomCard {
  34. /// h1 {}2
  35. /// p {}
  36. /// }
  37. /// })
  38. /// }
  39. ///
  40. /// #[derive(PartialEq, Props)]
  41. /// struct CardProps {
  42. /// children: Element
  43. /// }
  44. ///
  45. /// fn CustomCard(cx: Scope<CardProps>) -> Element {
  46. /// cx.render(rsx!{
  47. /// div {
  48. /// h1 {"Title card"}
  49. /// {cx.props.children}
  50. /// }
  51. /// })
  52. /// }
  53. /// ```
  54. impl<'a> Properties for FragmentProps<'a> {
  55. type Builder = FragmentBuilder<'a, false>;
  56. const IS_STATIC: bool = false;
  57. fn builder() -> Self::Builder {
  58. FragmentBuilder(None)
  59. }
  60. unsafe fn memoize(&self, _other: &Self) -> bool {
  61. false
  62. }
  63. }
  64. /// Create inline fragments using Component syntax.
  65. ///
  66. /// ## Details
  67. ///
  68. /// Fragments capture a series of children without rendering extra nodes.
  69. ///
  70. /// Creating fragments explicitly with the Fragment component is particularly useful when rendering lists or tables and
  71. /// a key is needed to identify each item.
  72. ///
  73. /// ## Example
  74. ///
  75. /// ```rust, ignore
  76. /// rsx!{
  77. /// Fragment { key: "abc" }
  78. /// }
  79. /// ```
  80. ///
  81. /// ## Usage
  82. ///
  83. /// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
  84. /// Try to avoid highly nested fragments if you can. Unlike React, there is no protection against infinitely nested fragments.
  85. ///
  86. /// This function defines a dedicated `Fragment` component that can be used to create inline fragments in the RSX macro.
  87. ///
  88. /// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
  89. #[allow(non_upper_case_globals, non_snake_case)]
  90. pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
  91. let i = cx.props.0.as_ref().map(|f| f.decouple());
  92. cx.render(LazyNodes::new(|f| f.fragment_from_iter(i)))
  93. }
  94. /// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
  95. /// on how to memoize the props and some additional optimizations that can be made. We strongly encourage using the
  96. /// derive macro to implement the `Properties` trait automatically as guarantee that your memoization strategy is safe.
  97. ///
  98. /// If your props are 'static, then Dioxus will require that they also be PartialEq for the derived memoize strategy. However,
  99. /// if your props borrow data, then the memoization strategy will simply default to "false" and the PartialEq will be ignored.
  100. /// This tends to be useful when props borrow something that simply cannot be compared (IE a reference to a closure);
  101. ///
  102. /// By default, the memoization strategy is very conservative, but can be tuned to be more aggressive manually. However,
  103. /// this is only safe if the props are 'static - otherwise you might borrow references after-free.
  104. ///
  105. /// We strongly suggest that any changes to memoization be done at the "PartialEq" level for 'static props. Additionally,
  106. /// we advise the use of smart pointers in cases where memoization is important.
  107. ///
  108. /// ## Example
  109. ///
  110. /// For props that are 'static:
  111. /// ```rust, ignore
  112. /// #[derive(Props, PartialEq)]
  113. /// struct MyProps {
  114. /// data: String
  115. /// }
  116. /// ```
  117. ///
  118. /// For props that borrow:
  119. ///
  120. /// ```rust, ignore
  121. /// #[derive(Props)]
  122. /// struct MyProps<'a >{
  123. /// data: &'a str
  124. /// }
  125. /// ```
  126. pub trait Properties: Sized {
  127. type Builder;
  128. const IS_STATIC: bool;
  129. fn builder() -> Self::Builder;
  130. /// Memoization can only happen if the props are valid for the 'static lifetime
  131. ///
  132. /// # Safety
  133. /// The user must know if their props are static, but if they make a mistake, UB happens
  134. /// Therefore it's unsafe to memoize.
  135. unsafe fn memoize(&self, other: &Self) -> bool;
  136. }
  137. impl Properties for () {
  138. type Builder = EmptyBuilder;
  139. const IS_STATIC: bool = true;
  140. fn builder() -> Self::Builder {
  141. EmptyBuilder {}
  142. }
  143. unsafe fn memoize(&self, _other: &Self) -> bool {
  144. true
  145. }
  146. }
  147. // We allow components to use the () generic parameter if they have no props. This impl enables the "build" method
  148. // that the macros use to anonymously complete prop construction.
  149. pub struct EmptyBuilder;
  150. impl EmptyBuilder {
  151. pub fn build(self) {}
  152. }
  153. /// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
  154. /// to initialize a component's props.
  155. pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder {
  156. T::builder()
  157. }