lifecycle.rs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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, |f| f);
  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, |f| f);
  92. *render_phase += 1;
  93. cx.render(match *render_phase {
  94. 0 => rsx!("Text0"),
  95. 1 => rsx!(div {}),
  96. 2 => rsx!("Text2"),
  97. 3 => rsx!(Child {}),
  98. 4 => rsx!({ None as Option<()> }),
  99. 5 => rsx!("text 3"),
  100. 6 => rsx!({ (0..2).map(|f| rsx!("text {f}")) }),
  101. 7 => 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. // simple_logger::init();
  196. static App: Component<()> = |cx| {
  197. let render_phase = cx.use_hook(|_| 0, |f| f);
  198. *render_phase += 1;
  199. cx.render(match *render_phase {
  200. 0 => rsx!(
  201. div {
  202. NavBar {}
  203. Dashboard {}
  204. }
  205. ),
  206. 1 => rsx!(
  207. div {
  208. NavBar {}
  209. Results {}
  210. }
  211. ),
  212. 2 => rsx!(
  213. div {
  214. NavBar {}
  215. Dashboard {}
  216. }
  217. ),
  218. 3 => rsx!(
  219. div {
  220. NavBar {}
  221. Results {}
  222. }
  223. ),
  224. 4 => rsx!(
  225. div {
  226. NavBar {}
  227. Dashboard {}
  228. }
  229. ),
  230. _ => rsx!("blah"),
  231. })
  232. };
  233. static NavBar: Component<()> = |cx| {
  234. println!("running navbar");
  235. cx.render(rsx! {
  236. h1 {
  237. "NavBar"
  238. {(0..3).map(|f| rsx!(NavLink {}))}
  239. }
  240. })
  241. };
  242. static NavLink: Component<()> = |cx| {
  243. println!("running navlink");
  244. cx.render(rsx! {
  245. h1 {
  246. "NavLink"
  247. }
  248. })
  249. };
  250. static Dashboard: Component<()> = |cx| {
  251. println!("running dashboard");
  252. cx.render(rsx! {
  253. div {
  254. "dashboard"
  255. }
  256. })
  257. };
  258. static Results: Component<()> = |cx| {
  259. println!("running results");
  260. cx.render(rsx! {
  261. div {
  262. "results"
  263. }
  264. })
  265. };
  266. let mut dom = VirtualDom::new(App);
  267. let edits = dom.rebuild();
  268. dbg!(&edits);
  269. let edits = dom.work_with_deadline(|| false);
  270. dbg!(&edits);
  271. let edits = dom.work_with_deadline(|| false);
  272. dbg!(&edits);
  273. let edits = dom.work_with_deadline(|| false);
  274. dbg!(&edits);
  275. let edits = dom.work_with_deadline(|| false);
  276. dbg!(&edits);
  277. }