state.rs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. use dioxus_core::VNode;
  2. use dioxus_core::*;
  3. use dioxus_core_macro::*;
  4. use dioxus_html as dioxus_elements;
  5. use dioxus_native_core::real_dom::*;
  6. #[derive(Debug, Clone, PartialEq, Default)]
  7. struct CallCounter(u32);
  8. impl BubbledUpState for CallCounter {
  9. type Ctx = ();
  10. fn reduce<'a, I>(&mut self, _children: I, _vnode: &VNode, _ctx: &mut Self::Ctx)
  11. where
  12. I: Iterator<Item = &'a Self>,
  13. Self: 'a,
  14. {
  15. self.0 += 1;
  16. }
  17. }
  18. impl PushedDownState for CallCounter {
  19. type Ctx = ();
  20. fn reduce(&mut self, _parent: Option<&Self>, _vnode: &VNode, _ctx: &mut Self::Ctx) {
  21. self.0 += 1;
  22. }
  23. }
  24. #[derive(Debug, Clone, PartialEq, Default)]
  25. struct BubbledUpStateTester(String, Vec<Box<BubbledUpStateTester>>);
  26. impl BubbledUpState for BubbledUpStateTester {
  27. type Ctx = u32;
  28. fn reduce<'a, I>(&mut self, children: I, vnode: &VNode, ctx: &mut Self::Ctx)
  29. where
  30. I: Iterator<Item = &'a Self>,
  31. Self: 'a,
  32. {
  33. assert_eq!(*ctx, 42);
  34. *self = BubbledUpStateTester(
  35. vnode.mounted_id().to_string(),
  36. children.map(|c| Box::new(c.clone())).collect(),
  37. );
  38. }
  39. }
  40. #[derive(Debug, Clone, PartialEq, Default)]
  41. struct PushedDownStateTester(String, Option<Box<PushedDownStateTester>>);
  42. impl PushedDownState for PushedDownStateTester {
  43. type Ctx = u32;
  44. fn reduce(&mut self, parent: Option<&Self>, vnode: &VNode, ctx: &mut Self::Ctx) {
  45. assert_eq!(*ctx, 42);
  46. *self = PushedDownStateTester(
  47. vnode.mounted_id().to_string(),
  48. parent.map(|c| Box::new(c.clone())),
  49. );
  50. }
  51. }
  52. #[test]
  53. fn tree_state_initial() {
  54. #[allow(non_snake_case)]
  55. fn Base(cx: Scope) -> Element {
  56. rsx!(cx, div {
  57. p{}
  58. h1{}
  59. })
  60. }
  61. let vdom = VirtualDom::new(Base);
  62. let mutations = vdom.create_vnodes(rsx! {
  63. div {
  64. p{}
  65. h1{}
  66. }
  67. });
  68. let mut tree: RealDom<BubbledUpStateTester, PushedDownStateTester> = RealDom::new();
  69. let nodes_updated = tree.apply_mutations(vec![mutations]);
  70. let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut 42, &mut 42);
  71. let root_div = &tree[1];
  72. assert_eq!(root_div.up_state.0, "1");
  73. assert_eq!(
  74. root_div.up_state.1,
  75. vec![
  76. Box::new(BubbledUpStateTester("2".to_string(), Vec::new())),
  77. Box::new(BubbledUpStateTester("3".to_string(), Vec::new()))
  78. ]
  79. );
  80. assert_eq!(root_div.down_state.0, "1");
  81. assert_eq!(root_div.down_state.1, None);
  82. let child_p = &tree[2];
  83. assert_eq!(child_p.up_state.0, "2");
  84. assert_eq!(child_p.up_state.1, Vec::new());
  85. assert_eq!(child_p.down_state.0, "2");
  86. assert_eq!(
  87. child_p.down_state.1,
  88. Some(Box::new(PushedDownStateTester("1".to_string(), None)))
  89. );
  90. let child_h1 = &tree[3];
  91. assert_eq!(child_h1.up_state.0, "3");
  92. assert_eq!(child_h1.up_state.1, Vec::new());
  93. assert_eq!(child_h1.down_state.0, "3");
  94. assert_eq!(
  95. child_h1.down_state.1,
  96. Some(Box::new(PushedDownStateTester("1".to_string(), None)))
  97. );
  98. }
  99. #[test]
  100. fn tree_state_reduce_initally_called_minimally() {
  101. #[derive(Debug, Clone, PartialEq, Default)]
  102. struct CallCounter(u32);
  103. impl BubbledUpState for CallCounter {
  104. type Ctx = ();
  105. fn reduce<'a, I>(&mut self, _children: I, _vnode: &VNode, _ctx: &mut Self::Ctx)
  106. where
  107. I: Iterator<Item = &'a Self>,
  108. Self: 'a,
  109. {
  110. self.0 += 1;
  111. }
  112. }
  113. impl PushedDownState for CallCounter {
  114. type Ctx = ();
  115. fn reduce(&mut self, _parent: Option<&Self>, _vnode: &VNode, _ctx: &mut Self::Ctx) {
  116. self.0 += 1;
  117. }
  118. }
  119. #[allow(non_snake_case)]
  120. fn Base(cx: Scope) -> Element {
  121. rsx!(cx, div {
  122. div{
  123. div{
  124. p{}
  125. }
  126. p{
  127. "hello"
  128. }
  129. div{
  130. h1{}
  131. }
  132. p{
  133. "world"
  134. }
  135. }
  136. })
  137. }
  138. let vdom = VirtualDom::new(Base);
  139. let mutations = vdom.create_vnodes(rsx! {
  140. div {
  141. div{
  142. div{
  143. p{}
  144. }
  145. p{
  146. "hello"
  147. }
  148. div{
  149. h1{}
  150. }
  151. p{
  152. "world"
  153. }
  154. }
  155. }
  156. });
  157. let mut tree: RealDom<CallCounter, CallCounter> = RealDom::new();
  158. let nodes_updated = tree.apply_mutations(vec![mutations]);
  159. let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ());
  160. tree.traverse_depth_first(|n| {
  161. assert_eq!(n.up_state.0, 1);
  162. assert_eq!(n.down_state.0, 1);
  163. });
  164. }
  165. #[test]
  166. fn tree_state_reduce_down_called_minimally_on_update() {
  167. #[allow(non_snake_case)]
  168. fn Base(cx: Scope) -> Element {
  169. rsx!(cx, div {
  170. div{
  171. div{
  172. p{}
  173. }
  174. p{
  175. "hello"
  176. }
  177. div{
  178. h1{}
  179. }
  180. p{
  181. "world"
  182. }
  183. }
  184. })
  185. }
  186. let vdom = VirtualDom::new(Base);
  187. let mutations = vdom.create_vnodes(rsx! {
  188. div {
  189. width: "100%",
  190. div{
  191. div{
  192. p{}
  193. }
  194. p{
  195. "hello"
  196. }
  197. div{
  198. h1{}
  199. }
  200. p{
  201. "world"
  202. }
  203. }
  204. }
  205. });
  206. let mut tree: RealDom<CallCounter, CallCounter> = RealDom::new();
  207. let nodes_updated = tree.apply_mutations(vec![mutations]);
  208. let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ());
  209. let nodes_updated = tree.apply_mutations(vec![Mutations {
  210. edits: vec![DomEdit::SetAttribute {
  211. root: 1,
  212. field: "width",
  213. value: "99%",
  214. ns: Some("style"),
  215. }],
  216. dirty_scopes: fxhash::FxHashSet::default(),
  217. refs: Vec::new(),
  218. }]);
  219. let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ());
  220. tree.traverse_depth_first(|n| {
  221. assert_eq!(n.down_state.0, 2);
  222. });
  223. }
  224. #[test]
  225. fn tree_state_reduce_up_called_minimally_on_update() {
  226. #[allow(non_snake_case)]
  227. fn Base(cx: Scope) -> Element {
  228. rsx!(cx, div {
  229. div{
  230. div{
  231. p{}
  232. }
  233. p{
  234. "hello"
  235. }
  236. div{
  237. h1{}
  238. }
  239. p{
  240. "world"
  241. }
  242. }
  243. })
  244. }
  245. let vdom = VirtualDom::new(Base);
  246. let mutations = vdom.create_vnodes(rsx! {
  247. div {
  248. width: "100%",
  249. div{
  250. div{
  251. p{}
  252. }
  253. p{
  254. "hello"
  255. }
  256. div{
  257. h1{}
  258. }
  259. p{
  260. "world"
  261. }
  262. }
  263. }
  264. });
  265. let mut tree: RealDom<CallCounter, CallCounter> = RealDom::new();
  266. let nodes_updated = tree.apply_mutations(vec![mutations]);
  267. let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ());
  268. let nodes_updated = tree.apply_mutations(vec![Mutations {
  269. edits: vec![DomEdit::SetAttribute {
  270. root: 4,
  271. field: "width",
  272. value: "99%",
  273. ns: Some("style"),
  274. }],
  275. dirty_scopes: fxhash::FxHashSet::default(),
  276. refs: Vec::new(),
  277. }]);
  278. let _to_rerender = tree.update_state(&vdom, nodes_updated, &mut (), &mut ());
  279. tree.traverse_depth_first(|n| {
  280. assert_eq!(n.up_state.0, if n.id.0 > 4 { 1 } else { 2 });
  281. });
  282. }