useTransition
在 React 中的應用useTransition
用來幫助管理可能阻塞 UI 的狀態更新。允許將某些更新標記為“過渡”,這些更新可以被更緊急的更新所打斷。這可以通過使 UI 更加響應來改善用戶體驗,即使在處理緩慢或複雜的狀態更新時。
useTransition
的簡介useTransition
Hook 返回一個包含兩個元素的陣列:
isPending
:一個布林值,表示當前是否有過渡正在進行中。startTransition
:一個函數,用來包裝應被視為過渡的狀態更新。const [isPending, startTransition] = useTransition();
useTransition
的基本用法和例子當你希望在不阻塞 UI 的情況下更新狀態時,可以將狀態更新包裝在 startTransition
函數內。以下是一個基本例子:
import React, { useState, useTransition } from 'react';
function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('home');
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
return (
<div>
<button onClick={() => selectTab('home')}>Home</button>
<button onClick={() => selectTab('profile')}>Profile</button>
<div>{isPending ? 'Loading...' : `Content for ${tab}`}</div>
</div>
);
}
通過將狀態更新標記為過渡,來確保更緊急的更新(如用戶輸入)優先處理。這有助於保持 UI 的響應性。
在這個例子中,輸入框中的輸入操作不會被狀態更新所阻塞。相反,狀態更新將被視為過渡來處理,確保輸入保持響應。
import React, { useState, useTransition } from 'react';
function App() {
const [isPending, startTransition] = useTransition();
const [value, setValue] = useState('');
const handleChange = (event) => {
startTransition(() => {
setValue(event.target.value);
});
};
return (
<div>
<input type="text" onChange={handleChange} value={value} />
{isPending && <p>Updating...</p>}
</div>
);
}
你也可以使用 useTransition
來更新父元件的狀態。以下是一個例子:
import React, { useState, useTransition } from 'react';
function ParentComponent() {
const [isPending, startTransition] = useTransition();
const [data, setData] = useState(null);
const fetchData = () => {
startTransition(() => {
// 模擬數據獲取
setTimeout(() => {
setData({ message: 'Data loaded' });
}, 2000);
});
};
return (
<div>
<button onClick={fetchData}>Load Data</button>
{isPending ? <p>Loading...</p> : <p>{data?.message}</p>}
</div>
);
}
要在過渡期間顯示錯誤,可以使用錯誤邊界。React 允許你在渲染階段捕捉錯誤,這可以使用錯誤邊界來實現。
import React, { useState, useTransition } from 'react';
function ErrorBoundary({ children }) {
return (
<React.Suspense fallback={<div>Loading...</div>}>
{children}
</React.Suspense>
);
}
function App() {
const [isPending, startTransition] = useTransition();
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const fetchData = () => {
startTransition(() => {
// 模擬數據獲取
setTimeout(() => {
try {
// 模擬一個錯誤
throw new Error('Failed to fetch data');
} catch (err) {
setError(err);
}
}, 2000);
});
};
return (
<ErrorBoundary>
<button onClick={fetchData}>Fetch Data</button>
{isPending && <p>Loading...</p>}
{error ? <p style={{ color: 'red' }}>{error.message}</p> : <p>{data}</p>}
</ErrorBoundary>
);
}
你可以使用 useTransition
返回的 isPending
布林值來向用戶表明當前處於過渡中。例如,選項卡按鈕可以有一個特殊的“待處理”視覺狀態:
function TabButton({ children, isActive, onClick }) {
const [isPending, startTransition] = useTransition();
// ...
if (isPending) {
return <b className="pending">{children}</b>;
}
// ...
}
在這個例子中,PostsTab
組件從啟用了 Suspense
的數據源中獲取數據。當你點擊“Posts”選項卡時,PostsTab
組件將掛起,導致最近的加載中的後備方案出現:
import { Suspense, useState } 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 [tab, setTab] = useState('about');
return (
<Suspense fallback={<h1>🌀 Loading...</h1>}>
<TabButton
isActive={tab === 'about'}
onClick={() => setTab('about')}
>
About
</TabButton>
<TabButton
isActive={tab === 'posts'}
onClick={() => setTab('posts')}
>
Posts
</TabButton>
<TabButton
isActive={tab === 'contact'}
onClick={() => setTab('contact')}
>
Contact
</TabButton>
<hr />
{tab === 'about' && <AboutTab />}
{tab === 'posts' && <PostsTab />}
{tab === 'contact' && <ContactTab />}
</Suspense>
);
}
隱藏整個選項卡容器以顯示加載指示器會導致用戶體驗不連貫。如果你將 useTransition
添加到 TabButton
中,你可以改為在選項卡按鈕中指示待處理狀態。
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>
);
}
過渡效果只會“等待”足夠長的時間來避免隱藏已經顯示的內容(例如選項卡容器)。如果“Posts”選項卡具有一個嵌套
Suspense
邊界,過渡效果將不會“等待”它。
避免使用 startTransition
來更新受控的輸入框。輸入框的狀態更新應保持同步,以確保立即反饋。
import React, { useState, useTransition } from 'react';
function App() {
const [text, setText] = useState('');
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
setText(e.target.value); // 同步更新以確保立即反饋
startTransition(() => {
// 任何其他可延遲的狀態更新
});
};
return <input value={text} onChange={handleChange} />;
}
確保狀態更新被包裝在 startTransition
函數調用內,且這些更新必須是同步
的。
startTransition(() => {
setState(newState);
});
有效使用 useTransition
可以通過以非阻塞的方式管理狀態更新,大大提升 React 應用程式的性能和響應性。通過將不太緊急的更新標記為過渡,你可以確保用戶界面更加順暢和響應。
其他關於hook系列文章:
關於useDeferredValue 關於useImperativeHandle 關於useSyncExternalStore 關於useTransition