update_state.rs 12 KB

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