React 團隊在 React Conf 2019 上展示了 Concurrent Mode 的更多細節。這是 React 16.8 Hooks 之後最重要的特性,但正式版可能要等到 2020 年。
Concurrent Mode 解決什麼問題
同步渲染的問題:React 開始渲染就不能被打斷,渲染大型組件樹時主線程被阻塞,用户交互無響應。
javascript
// 傳統同步渲染
// 開始渲染 → 渲染完成(中間用户點擊無響應)
ReactDOM.render(<App />, document.getElementById("root"));
// Concurrent Mode:渲染可以被打斷
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
// 渲染過程中可以響應用户輸入,高優先級任務可以打斷低優先級渲染
Suspense:數據獲取的異步化
jsx
// 之前的 Loading 模式(每個組件管理自己的 loading 狀態)
function UserProfile({ id }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser(id).then((user) => {
setUser(user);
setLoading(false);
});
}, [id]);
if (loading) return <Spinner />;
return <div>{user.name}</div>;
}
// Suspense 模式(組件只關心數據,Loading 在父層統一處理)
function UserProfile({ id }) {
// 假設 user 是通過 Suspense 兼容的 API 獲取的
const user = userResource.read(id); // 如果沒準備好,拋出 Promise
return <div>{user.name}</div>;
}
function App() {
return (
<Suspense fallback={<Spinner />}>
<UserProfile id="123" />
</Suspense>
);
}
useTransition:區分緊急和非緊急更新
jsx
import { useState, useTransition } from "react";
function SearchResults() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
function handleChange(e) {
// 緊急更新:立即顯示輸入的字符
setQuery(e.target.value);
// 非緊急更新:可以被打斷的搜索結果更新
startTransition(() => {
const searchResults = performExpensiveSearch(e.target.value);
setResults(searchResults);
});
}
return (
<div>
<input value={query} onChange={handleChange} />
{isPending ? <Spinner /> : null}
<ResultList results={results} />
</div>
);
}
useDeferredValue
jsx
import { useState, useDeferredValue } from "react";
function App() {
const [text, setText] = useState("");
// 延遲值:在 UI 響應緊急更新後,再更新這個值
const deferredText = useDeferredValue(text);
return (
<>
<input value={text} onChange={(e) => setText(e.target.value)} />
{/* 這個列表用延遲值,不會阻塞 input */}
<HeavyList query={deferredText} />
</>
);
}
2019 年的狀態
Concurrent Mode 在 2019 年還在實驗階段:
- 不穩定的 API(正式版 API 可能會變)
- 只在
ReactDOM.createRoot中啓用 - 依賴 Suspense 的數據獲取庫還不成熟(relay、swr 在跟進)
預計 2020-2021 年才會穩定。現在瞭解概念,為以後做準備。
小結
- Concurrent Mode:渲染可中斷,高優先級任務可打斷低優先級渲染
- Suspense:統一處理異步加載狀態,組件更純粹
- useTransition:區分緊急和非緊急狀態更新
- useDeferredValue:延遲非緊急值更新,類似 debounce 但更聰明
- 2019 年還是實驗特性,生產項目謹慎使用