本文正在参加「金石计划」
const [isPending, startTransition] = useTransition();
更新 state 界面不会出现卡顿
标记整个更新,通常框架和路由使用
-
isPending
是否有正在加载的 transition
-
startTransition
-
标记 state 更新函数为一个 transition 函数
-
transition 函数参数 scope
-
scope 函数没有参数,调用时同步标记 state 更新逻辑为 transition
-
内部逻辑只能是 state 更新逻辑
如果使用 props 或者其他 自定义 Hook,使用
useDeferredValue -
必须是同步更新 state,不能使用 timeout
-
-
state 更新标记会被其他 state 更新打断
-
不能用作控制文本输入
-
批量 transitions
-
state 更新成为 transition
import { useState, useTransition } from 'react';
import TabButton from './TabButton.js';
import AboutTab from './AboutTab.js';
import PostsTab from './PostsTab.js';
import ContactTab from './ContactTab.js';
export default function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
return (
<>
<TabButton
isActive={tab === 'about'}
onClick={() => selectTab('about')}
>
About
</TabButton>
<TabButton
isActive={tab === 'posts'}
onClick={() => selectTab('posts')}
>
Posts (slow)
</TabButton>
<TabButton
isActive={tab === 'contact'}
onClick={() => selectTab('contact')}
>
Contact
</TabButton>
<hr />
{tab === 'about' && <AboutTab />}
{tab === 'posts' && <PostsTab />}
{tab === 'contact' && <ContactTab />}
</>
);
}
父组件更新成为 transition
父组件更新 state 逻辑传递给子组件,子组件内部 state 更新逻辑变为 transition
export default function TabButton({ children, isActive, onClick }) {
const [isPending, startTransition] = useTransition();
if (isActive) {
return <b>{children}</b>
}
return (
<button onClick={() => {
startTransition(() => {
onClick();
});
}}>
{children}
</button>
);
}
transition 期间展示效果
import { useTransition } from 'react';
export default function TabButton({ children, isActive, onClick }) {
const [isPending, startTransition] = useTransition();
if (isActive) {
return <b>{children}</b>
}
if (isPending) {
return <b className="pending">{children}</b>;
}
return (
<button onClick={() => {
startTransition(() => {
onClick();
});
}}>
{children}
</button>
);
}
屏蔽不想要的 loading 状态
Suspense fallback
Suspense 不能嵌套
function App() {
const [isPending, startTransition] = useTransition();
const [count, setCount] = useState(0);
function handleClick() {
startTransition(() => {
setCount(c => c + 1);
})
}
return (
<div>
{isPending && <Spinner />}
<button onClick={handleClick}>{count}</button>
</div>
);
}
路由切换效果
路由切换逻辑成为一个 transition
import { Suspense, useState, useTransition } from 'react';
import IndexPage from './IndexPage.js';
import ArtistPage from './ArtistPage.js';
import Layout from './Layout.js';
export default function App() {
return (
<Suspense fallback={<BigSpinner />}>
<Router />
</Suspense>
);
}
function Router() {
const [page, setPage] = useState('/');
const [isPending, startTransition] = useTransition();
function navigate(url) {
startTransition(() => {
setPage(url);
});
}
let content;
if (page === '/') {
content = (
<IndexPage navigate={navigate} />
);
} else if (page === '/the-beatles') {
content = (
<ArtistPage
artist={{
id: 'the-beatles',
name: 'The Beatles',
}}
/>
);
}
return (
<Layout isPending={isPending}>
{content}
</Layout>
);
}
function BigSpinner() {
return <h2>🌀 Loading...</h2>;
}
问答
-
transition 更新 input 的数据不生效
-
state 更新没有被当成 transition
scope 函数是异步的
-
组件外边调用 useTransition
-
startTransition scope 函数立即执行
函数立即执行,标记 transitions 逻辑是在计划
startTransition
import { startTransition } from 'react';
startTransition(scope)
-
state 作为一个 transition 更新
import { startTransition } from 'react'; function TabContainer() { const [tab, setTab] = useState('about'); function selectTab(nextTab) { startTransition(() => { setTab(nextTab); }); } // ... } -
startTransition 不能监测是否是 pending 状态
-
可以在组件外部运行