lifecycle.rs 7.0 KB

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