state.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. use anymap::AnyMap;
  2. use dioxus_core::VNode;
  3. use dioxus_core::*;
  4. use dioxus_core_macro::*;
  5. use dioxus_html as dioxus_elements;
  6. use dioxus_native_core::real_dom::*;
  7. use dioxus_native_core::real_dom_new_api::{
  8. AttributeMask, ChildDepState, NodeDepState, NodeMask, NodeView, ParentDepState, State,
  9. };
  10. use dioxus_native_core_macro::State;
  11. #[derive(Debug, Clone, Default, State)]
  12. struct CallCounterState {
  13. #[child_dep_state(ChildDepCallCounter)]
  14. child_counter: ChildDepCallCounter,
  15. #[parent_dep_state(ParentDepCallCounter)]
  16. parent_counter: ParentDepCallCounter,
  17. #[node_dep_state()]
  18. node_counter: NodeDepCallCounter,
  19. }
  20. #[derive(Debug, Clone, Default)]
  21. struct ChildDepCallCounter(u32);
  22. impl ChildDepState for ChildDepCallCounter {
  23. type Ctx = ();
  24. type DepState = Self;
  25. const NODE_MASK: NodeMask = NodeMask::ALL;
  26. fn reduce(
  27. &mut self,
  28. _node: NodeView,
  29. _children: Vec<&Self::DepState>,
  30. _ctx: &Self::Ctx,
  31. ) -> bool {
  32. self.0 += 1;
  33. true
  34. }
  35. }
  36. #[derive(Debug, Clone, Default)]
  37. struct ParentDepCallCounter(u32);
  38. impl ParentDepState for ParentDepCallCounter {
  39. type Ctx = ();
  40. type DepState = Self;
  41. const NODE_MASK: NodeMask = NodeMask::ALL;
  42. fn reduce(
  43. &mut self,
  44. _node: NodeView,
  45. _parent: Option<&Self::DepState>,
  46. _ctx: &Self::Ctx,
  47. ) -> bool {
  48. self.0 += 1;
  49. true
  50. }
  51. }
  52. #[derive(Debug, Clone, Default)]
  53. struct NodeDepCallCounter(u32);
  54. impl NodeDepState for NodeDepCallCounter {
  55. type Ctx = ();
  56. const NODE_MASK: NodeMask = NodeMask::ALL;
  57. fn reduce(&mut self, _node: NodeView, _ctx: &Self::Ctx) -> bool {
  58. self.0 += 1;
  59. true
  60. }
  61. }
  62. #[derive(Debug, Clone, PartialEq, Default)]
  63. struct BubbledUpStateTester(Option<String>, Vec<Box<BubbledUpStateTester>>);
  64. impl ChildDepState for BubbledUpStateTester {
  65. type Ctx = u32;
  66. type DepState = Self;
  67. const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, true, false);
  68. fn reduce(&mut self, node: NodeView, children: Vec<&Self::DepState>, ctx: &Self::Ctx) -> bool {
  69. assert_eq!(*ctx, 42);
  70. *self = BubbledUpStateTester(
  71. node.tag().map(|s| s.to_string()),
  72. children.into_iter().map(|c| Box::new(c.clone())).collect(),
  73. );
  74. true
  75. }
  76. }
  77. #[derive(Debug, Clone, PartialEq, Default)]
  78. struct PushedDownStateTester(Option<String>, Option<Box<PushedDownStateTester>>);
  79. impl ParentDepState for PushedDownStateTester {
  80. type Ctx = u32;
  81. type DepState = Self;
  82. const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::NONE, true, false);
  83. fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool {
  84. assert_eq!(*ctx, 42);
  85. *self = PushedDownStateTester(
  86. node.tag().map(|s| s.to_string()),
  87. parent.map(|c| Box::new(c.clone())),
  88. );
  89. true
  90. }
  91. }
  92. #[derive(State, Clone, Default, Debug)]
  93. struct StateTester {
  94. #[child_dep_state(BubbledUpStateTester, u32)]
  95. bubbled: BubbledUpStateTester,
  96. #[parent_dep_state(PushedDownStateTester, u32)]
  97. pushed: PushedDownStateTester,
  98. #[node_dep_state(u32)]
  99. node: NodeStateTester,
  100. }
  101. #[derive(Debug, Clone, PartialEq, Default)]
  102. struct NodeStateTester(Option<String>, Vec<(String, String)>);
  103. impl NodeDepState for NodeStateTester {
  104. type Ctx = u32;
  105. const NODE_MASK: NodeMask = NodeMask::new(AttributeMask::All, true, false);
  106. fn reduce(&mut self, node: NodeView, ctx: &Self::Ctx) -> bool {
  107. assert_eq!(*ctx, 42);
  108. *self = NodeStateTester(
  109. node.tag().map(|s| s.to_string()),
  110. node.attributes()
  111. .map(|a| (a.name.to_string(), a.value.to_string()))
  112. .collect(),
  113. );
  114. true
  115. }
  116. }
  117. #[test]
  118. fn state_initial() {
  119. #[allow(non_snake_case)]
  120. fn Base(cx: Scope) -> Element {
  121. rsx!(cx, div {
  122. p{}
  123. h1{}
  124. })
  125. }
  126. let vdom = VirtualDom::new(Base);
  127. let mutations = vdom.create_vnodes(rsx! {
  128. div {
  129. p{
  130. color: "red"
  131. }
  132. h1{}
  133. }
  134. });
  135. let mut dom: RealDom<StateTester> = RealDom::new();
  136. let nodes_updated = dom.apply_mutations(vec![mutations]);
  137. let mut ctx = AnyMap::new();
  138. ctx.insert(42u32);
  139. let _to_rerender = dom.update_state(&vdom, nodes_updated, ctx);
  140. let root_div = &dom[1];
  141. assert_eq!(root_div.state.bubbled.0, Some("div".to_string()));
  142. assert_eq!(
  143. root_div.state.bubbled.1,
  144. vec![
  145. Box::new(BubbledUpStateTester(Some("p".to_string()), Vec::new())),
  146. Box::new(BubbledUpStateTester(Some("h1".to_string()), Vec::new()))
  147. ]
  148. );
  149. assert_eq!(root_div.state.pushed.0, Some("div".to_string()));
  150. assert_eq!(root_div.state.pushed.1, None);
  151. assert_eq!(root_div.state.node.0, Some("div".to_string()));
  152. assert_eq!(root_div.state.node.1, vec![]);
  153. let child_p = &dom[2];
  154. assert_eq!(child_p.state.bubbled.0, Some("p".to_string()));
  155. assert_eq!(child_p.state.bubbled.1, Vec::new());
  156. assert_eq!(child_p.state.pushed.0, Some("p".to_string()));
  157. assert_eq!(
  158. child_p.state.pushed.1,
  159. Some(Box::new(PushedDownStateTester(
  160. Some("div".to_string()),
  161. None
  162. )))
  163. );
  164. assert_eq!(child_p.state.node.0, Some("p".to_string()));
  165. assert_eq!(
  166. child_p.state.node.1,
  167. vec![("color".to_string(), "red".to_string())]
  168. );
  169. let child_h1 = &dom[3];
  170. assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string()));
  171. assert_eq!(child_h1.state.bubbled.1, Vec::new());
  172. assert_eq!(child_h1.state.pushed.0, Some("h1".to_string()));
  173. assert_eq!(
  174. child_h1.state.pushed.1,
  175. Some(Box::new(PushedDownStateTester(
  176. Some("div".to_string()),
  177. None
  178. )))
  179. );
  180. assert_eq!(child_h1.state.node.0, Some("h1".to_string()));
  181. assert_eq!(child_h1.state.node.1, vec![]);
  182. }
  183. #[test]
  184. fn state_reduce_initally_called_minimally() {
  185. #[allow(non_snake_case)]
  186. fn Base(cx: Scope) -> Element {
  187. rsx!(cx, div {
  188. div{
  189. div{
  190. p{}
  191. }
  192. p{
  193. "hello"
  194. }
  195. div{
  196. h1{}
  197. }
  198. p{
  199. "world"
  200. }
  201. }
  202. })
  203. }
  204. let vdom = VirtualDom::new(Base);
  205. let mutations = vdom.create_vnodes(rsx! {
  206. div {
  207. div{
  208. div{
  209. p{}
  210. }
  211. p{
  212. "hello"
  213. }
  214. div{
  215. h1{}
  216. }
  217. p{
  218. "world"
  219. }
  220. }
  221. }
  222. });
  223. let mut dom: RealDom<CallCounterState> = RealDom::new();
  224. let nodes_updated = dom.apply_mutations(vec![mutations]);
  225. let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
  226. dom.traverse_depth_first(|n| {
  227. assert_eq!(n.state.child_counter.0, 1);
  228. assert_eq!(n.state.parent_counter.0, 1);
  229. assert_eq!(n.state.node_counter.0, 1);
  230. });
  231. }
  232. #[test]
  233. fn state_reduce_down_called_minimally_on_update() {
  234. #[allow(non_snake_case)]
  235. fn Base(cx: Scope) -> Element {
  236. rsx!(cx, div {
  237. width: "100%",
  238. div{
  239. div{
  240. p{}
  241. }
  242. p{
  243. "hello"
  244. }
  245. div{
  246. h1{}
  247. }
  248. p{
  249. "world"
  250. }
  251. }
  252. })
  253. }
  254. let vdom = VirtualDom::new(Base);
  255. let mutations = vdom.create_vnodes(rsx! {
  256. div {
  257. width: "100%",
  258. div{
  259. div{
  260. p{}
  261. }
  262. p{
  263. "hello"
  264. }
  265. div{
  266. h1{}
  267. }
  268. p{
  269. "world"
  270. }
  271. }
  272. }
  273. });
  274. let mut dom: RealDom<CallCounterState> = RealDom::new();
  275. let nodes_updated = dom.apply_mutations(vec![mutations]);
  276. let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
  277. let nodes_updated = dom.apply_mutations(vec![Mutations {
  278. edits: vec![DomEdit::SetAttribute {
  279. root: 1,
  280. field: "width",
  281. value: "99%",
  282. ns: Some("style"),
  283. }],
  284. dirty_scopes: fxhash::FxHashSet::default(),
  285. refs: Vec::new(),
  286. }]);
  287. let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
  288. dom.traverse_depth_first(|n| {
  289. println!("{:?}", n.state);
  290. // assert_eq!(n.state.parent_counter.0, 2);
  291. });
  292. panic!()
  293. }
  294. #[test]
  295. fn state_reduce_up_called_minimally_on_update() {
  296. #[allow(non_snake_case)]
  297. fn Base(cx: Scope) -> Element {
  298. rsx!(cx, div {
  299. width: "100%",
  300. div{
  301. div{
  302. p{}
  303. }
  304. p{
  305. "hello"
  306. }
  307. div{
  308. h1{}
  309. }
  310. p{
  311. "world"
  312. }
  313. }
  314. })
  315. }
  316. let vdom = VirtualDom::new(Base);
  317. let mutations = vdom.create_vnodes(rsx! {
  318. div {
  319. width: "100%",
  320. div{
  321. div{
  322. p{}
  323. }
  324. p{
  325. "hello"
  326. }
  327. div{
  328. h1{}
  329. }
  330. p{
  331. "world"
  332. }
  333. }
  334. }
  335. });
  336. let mut dom: RealDom<CallCounterState> = RealDom::new();
  337. let nodes_updated = dom.apply_mutations(vec![mutations]);
  338. let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
  339. let nodes_updated = dom.apply_mutations(vec![Mutations {
  340. edits: vec![DomEdit::SetAttribute {
  341. root: 4,
  342. field: "width",
  343. value: "99%",
  344. ns: Some("style"),
  345. }],
  346. dirty_scopes: fxhash::FxHashSet::default(),
  347. refs: Vec::new(),
  348. }]);
  349. let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
  350. dom.traverse_depth_first(|n| {
  351. println!("{:?}", n.state);
  352. // assert_eq!(n.state.child_counter.0, if n.id.0 > 4 { 1 } else { 2 });
  353. });
  354. panic!()
  355. }