|
| 1 | +# `<Suspense/>` |
| 2 | + |
| 3 | +In the previous chapter, we showed how you can create a simple loading screen to show some fallback while a resource is loading. |
| 4 | + |
| 5 | +```rust |
| 6 | +let (count, set_count) = create_signal(cx, 0); |
| 7 | +let a = create_resource(cx, count, |count| async move { load_a(count).await }); |
| 8 | + |
| 9 | +view! { cx, |
| 10 | + <h1>"My Data"</h1> |
| 11 | + {move || match once.read(cx) { |
| 12 | + None => view! { cx, <p>"Loading..."</p> }.into_view(cx), |
| 13 | + Some(data) => view! { cx, <ShowData data/> }.into_view(cx) |
| 14 | + }} |
| 15 | +} |
| 16 | +``` |
| 17 | + |
| 18 | +But what if we have two resources, and want to wait for both of them? |
| 19 | + |
| 20 | +```rust |
| 21 | +let (count, set_count) = create_signal(cx, 0); |
| 22 | +let (count2, set_count2) = create_signal(cx, 0); |
| 23 | +let a = create_resource(cx, count, |count| async move { load_a(count).await }); |
| 24 | +let b = create_resource(cx, count2, |count| async move { load_b(count).await }); |
| 25 | + |
| 26 | +view! { cx, |
| 27 | + <h1>"My Data"</h1> |
| 28 | + {move || match (a.read(cx), b.read(cx)) { |
| 29 | + _ => view! { cx, <p>"Loading..."</p> }.into_view(cx), |
| 30 | + (Some(a), Some(b)) => view! { cx, |
| 31 | + <ShowA a/> |
| 32 | + <ShowA b/> |
| 33 | + }.into_view(cx) |
| 34 | + }} |
| 35 | +} |
| 36 | +``` |
| 37 | + |
| 38 | +That’s not _so_ bad, but it’s kind of annoying. What if we could invert the flow of control? |
| 39 | + |
| 40 | +The [`<Suspense/>`](https://docs.rs/leptos/latest/leptos/fn.Suspense.html) component lets us do exactly that. You give it a `fallback` prop and children, one or more of which usually involves reading from a resource. Reading from a resource “under” a `<Suspense/>` (i.e., in one of its children) registers that resource with the `<Suspense/>`. If it’s still waiting for resources to load, it shows the `fallback`. When they’ve all loaded, it shows the children. |
| 41 | + |
| 42 | +```rust |
| 43 | +let (count, set_count) = create_signal(cx, 0); |
| 44 | +let (count2, set_count2) = create_signal(cx, 0); |
| 45 | +let a = create_resource(cx, count, |count| async move { load_a(count).await }); |
| 46 | +let b = create_resource(cx, count2, |count| async move { load_b(count).await }); |
| 47 | + |
| 48 | +view! { cx, |
| 49 | + <h1>"My Data"</h1> |
| 50 | + <Suspense |
| 51 | + fallback=move || view! { cx, <p>"Loading..."</p> } |
| 52 | + > |
| 53 | + <h2>"My Data"</h2> |
| 54 | + <h3>"A"</h3> |
| 55 | + {move || { |
| 56 | + a.read(cx) |
| 57 | + .map(|a| view! { cx, <ShowA a/> }) |
| 58 | + }} |
| 59 | + <h3>"B"</h3> |
| 60 | + {move || { |
| 61 | + b.read(cx) |
| 62 | + .map(|b| view! { cx, <ShowB b/> }) |
| 63 | + }} |
| 64 | + </Suspense> |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +Every time one of the resources is reloading, the `"Loading..."` fallback will show again. |
| 69 | + |
| 70 | +This inversion of the flow of control makes it easier to add or remove individual resources, as you don’t need to handle the matching yourself. It also unlocks some massive performance improvements during server-side rendering, which we’ll talk about during a later chapter. |
| 71 | + |
| 72 | +<iframe src="https://codesandbox.io/p/sandbox/10-async-resources-4z0qt3?file=%2Fsrc%2Fmain.rs&selection=%5B%7B%22endColumn%22%3A1%2C%22endLineNumber%22%3A3%2C%22startColumn%22%3A1%2C%22startLineNumber%22%3A3%7D%5D" width="100%" height="1000px"></iframe> |
0 commit comments