lifecycle.rs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #![allow(unused, non_upper_case_globals)]
  2. //! Tests for the lifecycle of components.
  3. use dioxus::prelude::*;
  4. use dioxus_core as dioxus;
  5. use dioxus_core::DomEdit::*;
  6. use dioxus_core::ScopeId;
  7. use dioxus_core_macro::*;
  8. use dioxus_html as dioxus_elements;
  9. use std::sync::{Arc, Mutex};
  10. mod test_logging;
  11. const IS_LOGGING_ENABLED: bool = true;
  12. type Shared<T> = Arc<Mutex<T>>;
  13. #[test]
  14. fn manual_diffing() {
  15. struct AppProps {
  16. value: Shared<&'static str>,
  17. }
  18. static App: Component<AppProps> = |cx| {
  19. let val = cx.props.value.lock().unwrap();
  20. cx.render(rsx! { div { "{val}" } })
  21. };
  22. let value = Arc::new(Mutex::new("Hello"));
  23. let mut dom = VirtualDom::new_with_props(
  24. App,
  25. AppProps {
  26. value: value.clone(),
  27. },
  28. );
  29. let _ = dom.rebuild();
  30. *value.lock().unwrap() = "goodbye";
  31. let edits = dom.rebuild();
  32. log::debug!("edits: {:?}", edits);
  33. }
  34. #[test]
  35. fn events_generate() {
  36. static App: Component = |cx| {
  37. let count = cx.use_hook(|_| 0);
  38. let inner = match *count {
  39. 0 => {
  40. rsx! {
  41. div {
  42. onclick: move |_| *count += 1,
  43. div {
  44. "nested"
  45. }
  46. "Click me!"
  47. }
  48. }
  49. }
  50. _ => todo!(),
  51. };
  52. cx.render(inner)
  53. };
  54. let mut dom = VirtualDom::new(App);
  55. let mut channel = dom.get_scheduler_channel();
  56. assert!(dom.has_work());
  57. let edits = dom.rebuild();
  58. assert_eq!(
  59. edits.edits,
  60. [
  61. CreateElement {
  62. tag: "div",
  63. root: 1,
  64. },
  65. NewEventListener {
  66. event_name: "click",
  67. scope: ScopeId(0),
  68. root: 1,
  69. },
  70. CreateElement {
  71. tag: "div",
  72. root: 2,
  73. },
  74. CreateTextNode {
  75. text: "nested",
  76. root: 3,
  77. },
  78. AppendChildren { many: 1 },
  79. CreateTextNode {
  80. text: "Click me!",
  81. root: 4,
  82. },
  83. AppendChildren { many: 2 },
  84. AppendChildren { many: 1 },
  85. ]
  86. )
  87. }
  88. #[test]
  89. fn components_generate() {
  90. static App: Component = |cx| {
  91. let render_phase = cx.use_hook(|_| 0);
  92. *render_phase += 1;
  93. cx.render(match *render_phase {
  94. 1 => rsx!("Text0"),
  95. 2 => rsx!(div {}),
  96. 3 => rsx!("Text2"),
  97. 4 => rsx!(Child {}),
  98. 5 => rsx!({ None as Option<()> }),
  99. 6 => rsx!("text 3"),
  100. 7 => rsx!({ (0..2).map(|f| rsx!("text {f}")) }),
  101. 8 => rsx!(Child {}),
  102. _ => todo!(),
  103. })
  104. };
  105. static Child: Component = |cx| {
  106. cx.render(rsx! {
  107. h1 {}
  108. })
  109. };
  110. let mut dom = VirtualDom::new(App);
  111. let edits = dom.rebuild();
  112. assert_eq!(
  113. edits.edits,
  114. [
  115. CreateTextNode {
  116. text: "Text0",
  117. root: 1,
  118. },
  119. AppendChildren { many: 1 },
  120. ]
  121. );
  122. let edits = dom.hard_diff(ScopeId(0));
  123. assert_eq!(
  124. edits.edits,
  125. [
  126. CreateElement {
  127. tag: "div",
  128. root: 2,
  129. },
  130. ReplaceWith { root: 1, m: 1 },
  131. ]
  132. );
  133. let edits = dom.hard_diff(ScopeId(0));
  134. assert_eq!(
  135. edits.edits,
  136. [
  137. CreateTextNode {
  138. text: "Text2",
  139. root: 3,
  140. },
  141. ReplaceWith { root: 2, m: 1 },
  142. ]
  143. );
  144. let edits = dom.hard_diff(ScopeId(0));
  145. assert_eq!(
  146. edits.edits,
  147. [
  148. CreateElement { tag: "h1", root: 4 },
  149. ReplaceWith { root: 3, m: 1 },
  150. ]
  151. );
  152. let edits = dom.hard_diff(ScopeId(0));
  153. assert_eq!(
  154. edits.edits,
  155. [CreatePlaceholder { root: 5 }, ReplaceWith { root: 4, m: 1 },]
  156. );
  157. let edits = dom.hard_diff(ScopeId(0));
  158. assert_eq!(
  159. edits.edits,
  160. [
  161. CreateTextNode {
  162. text: "text 3",
  163. root: 6,
  164. },
  165. ReplaceWith { root: 5, m: 1 },
  166. ]
  167. );
  168. let edits = dom.hard_diff(ScopeId(0));
  169. assert_eq!(
  170. edits.edits,
  171. [
  172. CreateTextNode {
  173. text: "text 0",
  174. root: 7,
  175. },
  176. CreateTextNode {
  177. text: "text 1",
  178. root: 8,
  179. },
  180. ReplaceWith { root: 6, m: 2 },
  181. ]
  182. );
  183. let edits = dom.hard_diff(ScopeId(0));
  184. assert_eq!(
  185. edits.edits,
  186. [
  187. CreateElement { tag: "h1", root: 9 },
  188. ReplaceWith { root: 7, m: 1 },
  189. Remove { root: 8 },
  190. ]
  191. );
  192. }
  193. #[test]
  194. fn component_swap() {
  195. static App: Component = |cx| {
  196. let render_phase = cx.use_hook(|_| 0);
  197. *render_phase += 1;
  198. cx.render(match *render_phase {
  199. 0 => rsx!(
  200. div {
  201. NavBar {}
  202. Dashboard {}
  203. }
  204. ),
  205. 1 => rsx!(
  206. div {
  207. NavBar {}
  208. Results {}
  209. }
  210. ),
  211. 2 => rsx!(
  212. div {
  213. NavBar {}
  214. Dashboard {}
  215. }
  216. ),
  217. 3 => rsx!(
  218. div {
  219. NavBar {}
  220. Results {}
  221. }
  222. ),
  223. 4 => rsx!(
  224. div {
  225. NavBar {}
  226. Dashboard {}
  227. }
  228. ),
  229. _ => rsx!("blah"),
  230. })
  231. };
  232. static NavBar: Component = |cx| {
  233. println!("running navbar");
  234. cx.render(rsx! {
  235. h1 {
  236. "NavBar"
  237. {(0..3).map(|f| rsx!(NavLink {}))}
  238. }
  239. })
  240. };
  241. static NavLink: Component = |cx| {
  242. println!("running navlink");
  243. cx.render(rsx! {
  244. h1 {
  245. "NavLink"
  246. }
  247. })
  248. };
  249. static Dashboard: Component = |cx| {
  250. println!("running dashboard");
  251. cx.render(rsx! {
  252. div {
  253. "dashboard"
  254. }
  255. })
  256. };
  257. static Results: Component = |cx| {
  258. println!("running results");
  259. cx.render(rsx! {
  260. div {
  261. "results"
  262. }
  263. })
  264. };
  265. let mut dom = VirtualDom::new(App);
  266. let edits = dom.rebuild();
  267. dbg!(&edits);
  268. let edits = dom.work_with_deadline(|| false);
  269. dbg!(&edits);
  270. let edits = dom.work_with_deadline(|| false);
  271. dbg!(&edits);
  272. let edits = dom.work_with_deadline(|| false);
  273. dbg!(&edits);
  274. let edits = dom.work_with_deadline(|| false);
  275. dbg!(&edits);
  276. }