Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

<Transition/>

<Transition/>

你会注意到在 <Suspense/> 的例子中,如果你不断地重新加载数据,它会不停地闪回 "Loading..." 状态。有时这没问题,但在另一些时候,我们可以使用 <Transition/>

You’ll notice in the <Suspense/> example that if you keep reloading the data, it keeps flickering back to "Loading...". Sometimes this is fine. For other times, there’s <Transition/>.

<Transition/> 的行为与 <Suspense/> 完全相同,但它不是每次都回退到 fallback,而仅在第一次加载时显示 fallback。在随后的所有加载中,它会继续显示旧数据,直到新数据准备就绪。这对于防止闪烁效果以及允许用户继续与你的应用程序交互非常方便。

<Transition/> behaves exactly the same as <Suspense/>, but instead of falling back every time, it only shows the fallback the first time. On all subsequent loads, it continues showing the old data until the new data are ready. This can be really handy to prevent the flickering effect, and to allow users to continue interacting with your application.

这个例子展示了如何使用 <Transition/> 创建一个简单的标签式联系人列表。当你选择一个新标签时,它会继续显示当前的联系人,直到新数据加载完毕。这比不断回退到加载消息能提供好得多的用户体验。

This example shows how you can create a simple tabbed contact list with <Transition/>. When you select a new tab, it continues showing the current contact until the new data loads. This can be a much better user experience than constantly falling back to a loading message.

在线示例

点击打开 CodeSandbox。

Click to open CodeSandbox.

CodeSandbox 源码 (CodeSandbox Source)
use gloo_timers::future::TimeoutFuture;
use leptos::prelude::*;

async fn important_api_call(id: usize) -> String {
    TimeoutFuture::new(1_000).await;
    match id {
        0 => "Alice",
        1 => "Bob",
        2 => "Carol",
        _ => "User not found",
    }
    .to_string()
}

#[component]
fn App() -> impl IntoView {
    let (tab, set_tab) = signal(0);
    let (pending, set_pending) = signal(false);

    // 每当 `tab` 变化时,这里都会重新加载
    // this will reload every time `tab` changes
    let user_data = LocalResource::new(move || important_api_call(tab.get()));

    view! {
        <div class="buttons">
            <button
                on:click=move |_| set_tab.set(0)
                class:selected=move || tab.get() == 0
            >
                "Tab A"
            </button>
            <button
                on:click=move |_| set_tab.set(1)
                class:selected=move || tab.get() == 1
            >
                "Tab B"
            </button>
            <button
                on:click=move |_| set_tab.set(2)
                class:selected=move || tab.get() == 2
            >
                "Tab C"
            </button>
        </div>
        <p>
            {move || if pending.get() {
                // 等一下...
                "Hang on..."
            } else {
                // 好了。
                "Ready."
            }}
        </p>
        <Transition
            // 回退内容(fallback)仅在最初显示
            // 在随后的重新加载中,当前的子组件将继续显示
            // the fallback will show initially
            // on subsequent reloads, the current child will
            // continue showing
            fallback=move || view! { <p>"Loading initial data..."</p> }
            // 每当过渡正在进行时,这里将被设置为 `true`
            // this will be set to `true` whenever the transition is ongoing
            set_pending
        >
            <p>
                {move || user_data.read().as_deref().map(ToString::to_string)}
            </p>
        </Transition>
    }
}

fn main() {
    leptos::mount::mount_to_body(App)
}