update_state.rs 12 KB

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