update_state.rs 12 KB

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