update_state.rs 14 KB

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