suspense.rs 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. //! Suspense in Dioxus
  2. //!
  3. //! Currently, `rsx!` does not accept futures as values. To achieve the functionality
  4. //! of suspense, we need to make a new component that performs its own suspense
  5. //! handling.
  6. //!
  7. //! In this example, we render the `Doggo` component which starts a future that
  8. //! will cause it to fetch a random dog image from the Dog API. Since the data
  9. //! is not ready immediately, we render some loading text.
  10. //!
  11. //! We can achieve the majority of suspense functionality by composing "suspenseful"
  12. //! primitives in our own custom components.
  13. use dioxus::desktop::{Config, LogicalSize, WindowBuilder};
  14. use dioxus::prelude::*;
  15. fn main() {
  16. dioxus::LaunchBuilder::new()
  17. .with_cfg(desktop! {
  18. Config::new().with_window(
  19. WindowBuilder::new()
  20. .with_title("Doggo Fetcher")
  21. .with_inner_size(LogicalSize::new(600.0, 800.0)),
  22. )
  23. })
  24. .launch(app)
  25. }
  26. fn app() -> Element {
  27. rsx! {
  28. div {
  29. h1 { "Dogs are very important" }
  30. p {
  31. "The dog or domestic dog (Canis familiaris[4][5] or Canis lupus familiaris[5])"
  32. "is a domesticated descendant of the wolf which is characterized by an upturning tail."
  33. "The dog derived from an ancient, extinct wolf,[6][7] and the modern grey wolf is the"
  34. "dog's nearest living relative.[8] The dog was the first species to be domesticated,[9][8]"
  35. "by hunter–gatherers over 15,000 years ago,[7] before the development of agriculture.[1]"
  36. }
  37. h3 { "Illustrious Dog Photo" }
  38. SuspenseBoundary {
  39. fallback: move |suspense: SuspenseContext| suspense.suspense_placeholder().unwrap_or_else(|| rsx! {
  40. div {
  41. "Loading..."
  42. }
  43. }),
  44. Doggo {}
  45. }
  46. }
  47. }
  48. }
  49. /// This component will re-render when the future has finished
  50. /// Suspense is achieved my moving the future into only the component that
  51. /// actually renders the data.
  52. #[component]
  53. fn Doggo() -> Element {
  54. let mut resource = use_resource(move || async move {
  55. #[derive(serde::Deserialize)]
  56. struct DogApi {
  57. message: String,
  58. }
  59. reqwest::get("https://dog.ceo/api/breeds/image/random/")
  60. .await
  61. .unwrap()
  62. .json::<DogApi>()
  63. .await
  64. });
  65. // You can suspend the future and only continue rendering when it's ready
  66. let value = resource.suspend().with_loading_placeholder(|| {
  67. rsx! {
  68. div {
  69. "Loading doggos..."
  70. }
  71. }
  72. })?;
  73. match value.read_unchecked().as_ref() {
  74. Ok(resp) => rsx! {
  75. button { onclick: move |_| resource.restart(), "Click to fetch another doggo" }
  76. div { img { max_width: "500px", max_height: "500px", src: "{resp.message}" } }
  77. },
  78. Err(_) => rsx! {
  79. div { "loading dogs failed" }
  80. button {
  81. onclick: move |_| resource.restart(),
  82. "retry"
  83. }
  84. },
  85. }
  86. }