update_state.rs 14 KB

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