update_state.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  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::node_ref::*;
  7. use dioxus_native_core::real_dom::*;
  8. use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
  9. use dioxus_native_core_macro::State;
  10. #[derive(Debug, Clone, Default, State)]
  11. struct CallCounterStatePart1 {
  12. #[child_dep_state(child_counter)]
  13. child_counter: ChildDepCallCounter,
  14. }
  15. #[derive(Debug, Clone, Default, State)]
  16. struct CallCounterStatePart2 {
  17. #[parent_dep_state(parent_counter)]
  18. parent_counter: ParentDepCallCounter,
  19. }
  20. #[derive(Debug, Clone, Default, State)]
  21. struct CallCounterStatePart3 {
  22. #[node_dep_state()]
  23. node_counter: NodeDepCallCounter,
  24. }
  25. #[derive(Debug, Clone, Default, State)]
  26. struct CallCounterState {
  27. #[child_dep_state(child_counter)]
  28. child_counter: ChildDepCallCounter,
  29. #[state]
  30. part2: CallCounterStatePart2,
  31. #[parent_dep_state(parent_counter)]
  32. parent_counter: ParentDepCallCounter,
  33. #[state]
  34. part1: CallCounterStatePart1,
  35. #[state]
  36. part3: CallCounterStatePart3,
  37. #[node_dep_state()]
  38. node_counter: NodeDepCallCounter,
  39. }
  40. #[derive(Debug, Clone, Default)]
  41. struct ChildDepCallCounter(u32);
  42. impl ChildDepState for ChildDepCallCounter {
  43. type Ctx = ();
  44. type DepState = Self;
  45. const NODE_MASK: NodeMask = NodeMask::ALL;
  46. fn reduce<'a>(
  47. &mut self,
  48. _: NodeView,
  49. _: impl Iterator<Item = &'a Self::DepState>,
  50. _: &Self::Ctx,
  51. ) -> bool
  52. where
  53. Self::DepState: 'a,
  54. {
  55. self.0 += 1;
  56. true
  57. }
  58. }
  59. #[derive(Debug, Clone, Default)]
  60. struct ParentDepCallCounter(u32);
  61. impl ParentDepState for ParentDepCallCounter {
  62. type Ctx = ();
  63. type DepState = Self;
  64. const NODE_MASK: NodeMask = NodeMask::ALL;
  65. fn reduce(
  66. &mut self,
  67. _node: NodeView,
  68. _parent: Option<&Self::DepState>,
  69. _ctx: &Self::Ctx,
  70. ) -> bool {
  71. self.0 += 1;
  72. true
  73. }
  74. }
  75. #[derive(Debug, Clone, Default)]
  76. struct NodeDepCallCounter(u32);
  77. impl NodeDepState for NodeDepCallCounter {
  78. type Ctx = ();
  79. type DepState = ();
  80. const NODE_MASK: NodeMask = NodeMask::ALL;
  81. fn reduce(&mut self, _node: NodeView, _sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
  82. self.0 += 1;
  83. true
  84. }
  85. }
  86. #[derive(Debug, Clone, PartialEq, Default)]
  87. struct BubbledUpStateTester(Option<String>, Vec<Box<BubbledUpStateTester>>);
  88. impl ChildDepState for BubbledUpStateTester {
  89. type Ctx = u32;
  90. type DepState = Self;
  91. const NODE_MASK: NodeMask = NodeMask::new().with_tag();
  92. fn reduce<'a>(
  93. &mut self,
  94. node: NodeView,
  95. children: impl Iterator<Item = &'a Self::DepState>,
  96. ctx: &Self::Ctx,
  97. ) -> bool
  98. where
  99. Self::DepState: 'a,
  100. {
  101. assert_eq!(*ctx, 42);
  102. *self = BubbledUpStateTester(
  103. node.tag().map(|s| s.to_string()),
  104. children.into_iter().map(|c| Box::new(c.clone())).collect(),
  105. );
  106. true
  107. }
  108. }
  109. #[derive(Debug, Clone, PartialEq, Default)]
  110. struct PushedDownStateTester(Option<String>, Option<Box<PushedDownStateTester>>);
  111. impl ParentDepState for PushedDownStateTester {
  112. type Ctx = u32;
  113. type DepState = Self;
  114. const NODE_MASK: NodeMask = NodeMask::new().with_tag();
  115. fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool {
  116. assert_eq!(*ctx, 42);
  117. *self = PushedDownStateTester(
  118. node.tag().map(|s| s.to_string()),
  119. parent.map(|c| Box::new(c.clone())),
  120. );
  121. true
  122. }
  123. }
  124. #[derive(Debug, Clone, PartialEq, Default)]
  125. struct NodeStateTester(Option<String>, Vec<(String, String)>);
  126. impl NodeDepState for NodeStateTester {
  127. type Ctx = u32;
  128. type DepState = ();
  129. const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::All).with_tag();
  130. fn reduce(&mut self, node: NodeView, _sibling: &Self::DepState, ctx: &Self::Ctx) -> bool {
  131. assert_eq!(*ctx, 42);
  132. *self = NodeStateTester(
  133. node.tag().map(|s| s.to_string()),
  134. node.attributes()
  135. .map(|a| (a.name.to_string(), a.value.to_string()))
  136. .collect(),
  137. );
  138. true
  139. }
  140. }
  141. #[derive(State, Clone, Default, Debug)]
  142. struct StateTester {
  143. #[child_dep_state(bubbled, u32)]
  144. bubbled: BubbledUpStateTester,
  145. #[parent_dep_state(pushed, u32)]
  146. pushed: PushedDownStateTester,
  147. #[node_dep_state(NONE, u32)]
  148. node: NodeStateTester,
  149. }
  150. #[test]
  151. fn state_initial() {
  152. #[allow(non_snake_case)]
  153. fn Base(cx: Scope) -> Element {
  154. rsx!(cx, div {
  155. p{}
  156. h1{}
  157. })
  158. }
  159. let vdom = VirtualDom::new(Base);
  160. let mutations = vdom.create_vnodes(rsx! {
  161. div {
  162. p{
  163. color: "red"
  164. }
  165. h1{}
  166. }
  167. });
  168. let mut dom: RealDom<StateTester> = RealDom::new();
  169. let nodes_updated = dom.apply_mutations(vec![mutations]);
  170. let mut ctx = AnyMap::new();
  171. ctx.insert(42u32);
  172. let _to_rerender = dom.update_state(&vdom, nodes_updated, ctx);
  173. let root_div = &dom[1];
  174. assert_eq!(root_div.state.bubbled.0, Some("div".to_string()));
  175. assert_eq!(
  176. root_div.state.bubbled.1,
  177. vec![
  178. Box::new(BubbledUpStateTester(Some("p".to_string()), Vec::new())),
  179. Box::new(BubbledUpStateTester(Some("h1".to_string()), Vec::new()))
  180. ]
  181. );
  182. assert_eq!(root_div.state.pushed.0, Some("div".to_string()));
  183. assert_eq!(
  184. root_div.state.pushed.1,
  185. Some(Box::new(PushedDownStateTester(None, None)))
  186. );
  187. assert_eq!(root_div.state.node.0, Some("div".to_string()));
  188. assert_eq!(root_div.state.node.1, vec![]);
  189. let child_p = &dom[2];
  190. assert_eq!(child_p.state.bubbled.0, Some("p".to_string()));
  191. assert_eq!(child_p.state.bubbled.1, Vec::new());
  192. assert_eq!(child_p.state.pushed.0, Some("p".to_string()));
  193. assert_eq!(
  194. child_p.state.pushed.1,
  195. Some(Box::new(PushedDownStateTester(
  196. Some("div".to_string()),
  197. Some(Box::new(PushedDownStateTester(None, None)))
  198. )))
  199. );
  200. assert_eq!(child_p.state.node.0, Some("p".to_string()));
  201. assert_eq!(
  202. child_p.state.node.1,
  203. vec![("color".to_string(), "red".to_string())]
  204. );
  205. let child_h1 = &dom[3];
  206. assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string()));
  207. assert_eq!(child_h1.state.bubbled.1, Vec::new());
  208. assert_eq!(child_h1.state.pushed.0, Some("h1".to_string()));
  209. assert_eq!(
  210. child_h1.state.pushed.1,
  211. Some(Box::new(PushedDownStateTester(
  212. Some("div".to_string()),
  213. Some(Box::new(PushedDownStateTester(None, None)))
  214. )))
  215. );
  216. assert_eq!(child_h1.state.node.0, Some("h1".to_string()));
  217. assert_eq!(child_h1.state.node.1, vec![]);
  218. }
  219. #[test]
  220. fn state_reduce_initally_called_minimally() {
  221. #[allow(non_snake_case)]
  222. fn Base(cx: Scope) -> Element {
  223. rsx!(cx, div {
  224. div{
  225. div{
  226. p{}
  227. }
  228. p{
  229. "hello"
  230. }
  231. div{
  232. h1{}
  233. }
  234. p{
  235. "world"
  236. }
  237. }
  238. })
  239. }
  240. let vdom = VirtualDom::new(Base);
  241. let mutations = vdom.create_vnodes(rsx! {
  242. div {
  243. div{
  244. div{
  245. p{}
  246. }
  247. p{
  248. "hello"
  249. }
  250. div{
  251. h1{}
  252. }
  253. p{
  254. "world"
  255. }
  256. }
  257. }
  258. });
  259. let mut dom: RealDom<CallCounterState> = RealDom::new();
  260. let nodes_updated = dom.apply_mutations(vec![mutations]);
  261. let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
  262. dom.traverse_depth_first(|n| {
  263. assert_eq!(n.state.part1.child_counter.0, 1);
  264. assert_eq!(n.state.child_counter.0, 1);
  265. assert_eq!(n.state.part2.parent_counter.0, 1);
  266. assert_eq!(n.state.parent_counter.0, 1);
  267. assert_eq!(n.state.part3.node_counter.0, 1);
  268. assert_eq!(n.state.node_counter.0, 1);
  269. });
  270. }
  271. #[test]
  272. fn state_reduce_parent_called_minimally_on_update() {
  273. #[allow(non_snake_case)]
  274. fn Base(cx: Scope) -> Element {
  275. rsx!(cx, div {
  276. width: "100%",
  277. div{
  278. div{
  279. p{}
  280. }
  281. p{
  282. "hello"
  283. }
  284. div{
  285. h1{}
  286. }
  287. p{
  288. "world"
  289. }
  290. }
  291. })
  292. }
  293. let vdom = VirtualDom::new(Base);
  294. let mutations = vdom.create_vnodes(rsx! {
  295. div {
  296. width: "100%",
  297. div{
  298. div{
  299. p{}
  300. }
  301. p{
  302. "hello"
  303. }
  304. div{
  305. h1{}
  306. }
  307. p{
  308. "world"
  309. }
  310. }
  311. }
  312. });
  313. let mut dom: RealDom<CallCounterState> = RealDom::new();
  314. let nodes_updated = dom.apply_mutations(vec![mutations]);
  315. let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
  316. let nodes_updated = dom.apply_mutations(vec![Mutations {
  317. edits: vec![DomEdit::SetAttribute {
  318. root: 1,
  319. field: "width",
  320. value: "99%",
  321. ns: Some("style"),
  322. }],
  323. dirty_scopes: fxhash::FxHashSet::default(),
  324. refs: Vec::new(),
  325. }]);
  326. let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
  327. dom.traverse_depth_first(|n| {
  328. assert_eq!(n.state.part2.parent_counter.0, 2);
  329. assert_eq!(n.state.parent_counter.0, 2);
  330. });
  331. }
  332. #[test]
  333. fn state_reduce_child_called_minimally_on_update() {
  334. #[allow(non_snake_case)]
  335. fn Base(cx: Scope) -> Element {
  336. rsx!(cx, div {
  337. div{
  338. div{
  339. p{
  340. width: "100%",
  341. }
  342. }
  343. p{
  344. "hello"
  345. }
  346. div{
  347. h1{}
  348. }
  349. p{
  350. "world"
  351. }
  352. }
  353. })
  354. }
  355. let vdom = VirtualDom::new(Base);
  356. let mutations = vdom.create_vnodes(rsx! {
  357. div {
  358. div{
  359. div{
  360. p{
  361. width: "100%",
  362. }
  363. }
  364. p{
  365. "hello"
  366. }
  367. div{
  368. h1{}
  369. }
  370. p{
  371. "world"
  372. }
  373. }
  374. }
  375. });
  376. let mut dom: RealDom<CallCounterState> = RealDom::new();
  377. let nodes_updated = dom.apply_mutations(vec![mutations]);
  378. let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
  379. let nodes_updated = dom.apply_mutations(vec![Mutations {
  380. edits: vec![DomEdit::SetAttribute {
  381. root: 4,
  382. field: "width",
  383. value: "99%",
  384. ns: Some("style"),
  385. }],
  386. dirty_scopes: fxhash::FxHashSet::default(),
  387. refs: Vec::new(),
  388. }]);
  389. let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
  390. dom.traverse_depth_first(|n| {
  391. println!("{:?}", n);
  392. assert_eq!(
  393. n.state.part1.child_counter.0,
  394. if n.id.0 > 4 { 1 } else { 2 }
  395. );
  396. assert_eq!(n.state.child_counter.0, if n.id.0 > 4 { 1 } else { 2 });
  397. });
  398. }
  399. #[derive(Debug, Clone, Default, State)]
  400. struct UnorderedDependanciesState {
  401. #[node_dep_state(c)]
  402. b: BDepCallCounter,
  403. #[node_dep_state()]
  404. c: CDepCallCounter,
  405. #[node_dep_state(b)]
  406. a: ADepCallCounter,
  407. }
  408. #[derive(Debug, Clone, Default, PartialEq)]
  409. struct ADepCallCounter(usize, BDepCallCounter);
  410. impl NodeDepState for ADepCallCounter {
  411. type Ctx = ();
  412. type DepState = BDepCallCounter;
  413. const NODE_MASK: NodeMask = NodeMask::NONE;
  414. fn reduce(&mut self, _node: NodeView, sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
  415. self.0 += 1;
  416. self.1 = sibling.clone();
  417. true
  418. }
  419. }
  420. #[derive(Debug, Clone, Default, PartialEq)]
  421. struct BDepCallCounter(usize, CDepCallCounter);
  422. impl NodeDepState for BDepCallCounter {
  423. type Ctx = ();
  424. type DepState = CDepCallCounter;
  425. const NODE_MASK: NodeMask = NodeMask::NONE;
  426. fn reduce(&mut self, _node: NodeView, sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
  427. self.0 += 1;
  428. self.1 = sibling.clone();
  429. true
  430. }
  431. }
  432. #[derive(Debug, Clone, Default, PartialEq)]
  433. struct CDepCallCounter(usize);
  434. impl NodeDepState for CDepCallCounter {
  435. type Ctx = ();
  436. type DepState = ();
  437. const NODE_MASK: NodeMask = NodeMask::ALL;
  438. fn reduce(&mut self, _node: NodeView, _sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
  439. self.0 += 1;
  440. true
  441. }
  442. }
  443. #[test]
  444. fn dependancies_order_independant() {
  445. #[allow(non_snake_case)]
  446. fn Base(cx: Scope) -> Element {
  447. rsx!(cx, div {
  448. width: "100%",
  449. p{
  450. "hello"
  451. }
  452. })
  453. }
  454. let vdom = VirtualDom::new(Base);
  455. let mutations = vdom.create_vnodes(rsx! {
  456. div {
  457. width: "100%",
  458. p{
  459. "hello"
  460. }
  461. }
  462. });
  463. let mut dom: RealDom<UnorderedDependanciesState> = RealDom::new();
  464. let nodes_updated = dom.apply_mutations(vec![mutations]);
  465. let _to_rerender = dom.update_state(&vdom, nodes_updated, AnyMap::new());
  466. let c = CDepCallCounter(1);
  467. let b = BDepCallCounter(1, c.clone());
  468. let a = ADepCallCounter(1, b.clone());
  469. dom.traverse_depth_first(|n| {
  470. assert_eq!(&n.state.a, &a);
  471. assert_eq!(&n.state.b, &b);
  472. assert_eq!(&n.state.c, &c);
  473. });
  474. }
  475. #[derive(Clone, Default, State)]
  476. struct DependanciesStateTest {
  477. #[node_dep_state(c)]
  478. b: BDepCallCounter,
  479. #[node_dep_state()]
  480. c: CDepCallCounter,
  481. #[node_dep_state(b)]
  482. a: ADepCallCounter,
  483. #[state]
  484. child: UnorderedDependanciesState,
  485. }