update_state.rs 11 KB

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