news 2026/6/9 2:20:54

02-Hooks完全指南——08-useTransition 与 useDeferredValue

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
02-Hooks完全指南——08-useTransition 与 useDeferredValue

useTransition 与 useDeferredValue

一、React 18 并发特性

1.1 什么是并发渲染?

并发渲染允许 React 在渲染过程中中断、暂停、恢复或放弃渲染,从而保持 UI 响应性。

1.2 两个核心 Hook

Hook用途适用场景
useTransition标记非紧急更新页面切换、搜索过滤
useDeferredValue延迟更新某个值输入框实时搜索

二、useTransition

2.1 基本语法

const [isPending, startTransition] = useTransition();
  • isPending:布尔值,表示是否有进行中的 transition
  • startTransition:将更新标记为非紧急的函数

2.2 基础示例

function TabSwitcher() { const [tab, setTab] = useState('home'); const [isPending, startTransition] = useTransition(); const handleTabChange = (newTab) => { // 标记为非紧急更新 startTransition(() => { setTab(newTab); }); }; return ( <div> <button onClick={() => handleTabChange('home')}>首页</button> <button onClick={() => handleTabChange('profile')}>个人资料</button> <button onClick={() => handleTabChange('settings')}>设置</button> {isPending && <div>加载中...</div>} <div> {tab === 'home' && <HomeTab />} {tab === 'profile' && <ProfileTab />} {tab === 'settings' && <SettingsTab />} </div> </div> ); }

2.3 搜索过滤示例

function SearchPage() { const [query, setQuery] = useState(''); const [filteredList, setFilteredList] = useState([]); const [isPending, startTransition] = useTransition(); const allItems = Array.from({ length: 10000 }, (_, i) => `项目 ${i}`); const handleSearch = (e) => { const value = e.target.value; setQuery(value); // 过滤操作标记为低优先级 startTransition(() => { const filtered = allItems.filter(item => item.toLowerCase().includes(value.toLowerCase()) ); setFilteredList(filtered); }); }; return ( <div> <input type="text" value={query} onChange={handleSearch} placeholder="搜索..." /> {isPending && <div>正在搜索...</div>} <ul> {filteredList.map(item => ( <li key={item}>{item}</li> ))} </ul> </div> ); }

2.4 路由切换优化

function Router() { const [page, setPage] = useState('home'); const [isPending, startTransition] = useTransition(); const navigate = (newPage) => { startTransition(() => { setPage(newPage); }); }; return ( <div> <nav> <button onClick={() => navigate('home')}>首页</button> <button onClick={() => navigate('about')}>关于</button> <button onClick={() => navigate('contact')}>联系</button> </nav> {isPending && ( <div className="loading-overlay"> <Spinner /> </div> )} <Suspense fallback={<PageSkeleton />}> {page === 'home' && <HomePage />} {page === 'about' && <AboutPage />} {page === 'contact' && <ContactPage />} </Suspense> </div> ); }

三、useDeferredValue

3.1 基本语法

const deferredValue = useDeferredValue(value);
  • 返回一个延迟更新的值
  • 在紧急更新完成后才会更新

3.2 基础示例

function SearchWithDeferred() { const [query, setQuery] = useState(''); const deferredQuery = useDeferredValue(query); // 使用延迟的 query 进行搜索 const results = useMemo(() => { return performExpensiveSearch(deferredQuery); }, [deferredQuery]); return ( <div> <input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="输入搜索..." /> {/* 显示提示 */} {deferredQuery !== query && <div>正在更新列表...</div>} <List results={results} /> </div> ); }

3.3 实时搜索优化

function RealTimeSearch() { const [searchTerm, setSearchTerm] = useState(''); const deferredSearchTerm = useDeferredValue(searchTerm); // 昂贵的过滤操作 const filteredProducts = useMemo(() => { console.log('过滤产品...'); return products.filter(product => product.name.toLowerCase().includes(deferredSearchTerm.toLowerCase()) ); }, [deferredSearchTerm]); return ( <div> <input type="text" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder="搜索产品..." /> {/* 视觉反馈 */} <div> 搜索: {searchTerm} {deferredSearchTerm !== searchTerm && " (更新中...)"} </div> <div className="product-grid"> {filteredProducts.map(product => ( <ProductCard key={product.id} product={product} /> ))} </div> </div> ); }

四、useTransition vs useDeferredValue

4.1 区别对比

特性useTransitionuseDeferredValue
控制方式主动标记更新被动延迟值
返回值[isPending, startTransition]deferredValue
适用场景可控制的更新接收外部传入的值
加载指示isPending需要手动比较

4.2 选择建议

// 场景1:可以控制更新时,使用 useTransition function ControlledSearch() { const [query, setQuery] = useState(''); const [isPending, startTransition] = useTransition(); const handleChange = (e) => { const value = e.target.value; setQuery(value); startTransition(() => { // 执行搜索 }); }; } // 场景2:接收 props 或无法控制时,使用 useDeferredValue function UncontrolledSearch({ searchTerm }) { const deferredTerm = useDeferredValue(searchTerm); // 使用 deferredTerm 进行搜索 }

五、实战案例

5.1 大数据表格渲染

function LargeDataTable({ data }) { const [sortField, setSortField] = useState('id'); const [sortDirection, setSortDirection] = useState('asc'); const [isPending, startTransition] = useTransition(); const handleSort = (field) => { startTransition(() => { setSortField(field); setSortDirection(prev => field === sortField && prev === 'asc' ? 'desc' : 'asc' ); }); }; const sortedData = useMemo(() => { return [...data].sort((a, b) => { const aVal = a[sortField]; const bVal = b[sortField]; if (sortDirection === 'asc') { return aVal > bVal ? 1 : -1; } return aVal < bVal ? 1 : -1; }); }, [data, sortField, sortDirection]); return ( <div> {isPending && <div className="sorting-indicator">排序中...</div>} <table> <thead> <tr> <th onClick={() => handleSort('id')}>ID</th> <th onClick={() => handleSort('name')}>姓名</th> <th onClick={() => handleSort('age')}>年龄</th> </tr> </thead> <tbody> {sortedData.map(row => ( <tr key={row.id}> <td>{row.id}</td> <td>{row.name}</td> <td>{row.age}</td> </tr> ))} </tbody> </table> </div> ); }

5.2 仪表盘数据刷新

function Dashboard() { const [timeRange, setTimeRange] = useState('day'); const [isPending, startTransition] = useTransition(); const handleTimeRangeChange = (range) => { // 立即更新 UI 显示 setTimeRange(range); // 延迟数据获取 startTransition(() => { fetchDashboardData(range); }); }; return ( <div> <div className="controls"> <button onClick={() => handleTimeRangeChange('day')}>日</button> <button onClick={() => handleTimeRangeChange('week')}>周</button> <button onClick={() => handleTimeRangeChange('month')}>月</button> </div> {isPending && <SkeletonLoader />} <DashboardContent /> </div> ); }

5.3 自动完成输入框

function Autocomplete() { const [input, setInput] = useState(''); const deferredInput = useDeferredValue(input); const [suggestions, setSuggestions] = useState([]); const [isLoading, setIsLoading] = useState(false); useEffect(() => { if (!deferredInput) { setSuggestions([]); return; } setIsLoading(true); fetch(`/api/suggestions?q=${deferredInput}`) .then(res => res.json()) .then(data => { setSuggestions(data); setIsLoading(false); }); }, [deferredInput]); return ( <div className="autocomplete"> <input value={input} onChange={(e) => setInput(e.target.value)} placeholder="输入内容..." /> {input !== deferredInput && <div className="typing-indicator">输入中...</div>} {isLoading ? ( <div>加载建议...</div> ) : suggestions.length > 0 && ( <ul className="suggestions"> {suggestions.map(suggestion => ( <li key={suggestion.id}>{suggestion.text}</li> ))} </ul> )} </div> ); }

六、性能优化技巧

6.1 结合 useMemo 使用

function OptimizedSearch() { const [query, setQuery] = useState(''); const deferredQuery = useDeferredValue(query); // ✅ 使用 useMemo 缓存计算结果 const results = useMemo(() => { return expensiveSearch(deferredQuery); }, [deferredQuery]); return ( <div> <input value={query} onChange={(e) => setQuery(e.target.value)} /> <Results data={results} /> </div> ); }

6.2 使用 startTransition 包装

function BetterTransition() { const [isPending, startTransition] = useTransition(); const handleUpdate = () => { // 紧急更新:更新 UI 状态 setUiState('loading'); // 非紧急更新:数据处理 startTransition(() => { setData(processLargeData()); }); }; }

七、常见陷阱

7.1 不必要的 useTransition

// ❌ 简单更新不需要 useTransition const [isPending, startTransition] = useTransition(); startTransition(() => { setCount(count + 1); // 太简单,不需要 }); // ✅ 只对昂贵的更新使用 startTransition(() => { setFilteredList(expensiveFilter(allItems, query)); });

7.2 忘记处理 isPending

function MissingPending() { const [isPending, startTransition] = useTransition(); const [data, setData] = useState([]); const handleSearch = (query) => { startTransition(() => { setData(searchData(query)); }); }; // ❌ 用户不知道正在更新 return <div>{/* 没有加载指示器 */}</div>; } // ✅ 提供视觉反馈 function WithPending() { const [isPending, startTransition] = useTransition(); return ( <div> {isPending && <Spinner />} {/* 内容 */} </div> ); }

八、练习题

基础题

  1. 实现一个标签页切换,使用 useTransition 优化
  2. 实现一个搜索框,使用 useDeferredValue 优化

进阶题

  1. 实现一个带自动完成功能的搜索框
  2. 实现一个大表格的排序功能,使用 useTransition

参考答案

// 标签页切换优化 function OptimizedTabs() { const [tab, setTab] = useState('home'); const [isPending, startTransition] = useTransition(); const tabs = ['home', 'products', 'about', 'contact']; const handleTabChange = (newTab) => { startTransition(() => { setTab(newTab); }); }; return ( <div> <div className="tabs"> {tabs.map(t => ( <button key={t} onClick={() => handleTabChange(t)} className={tab === t ? 'active' : ''} > {t} </button> ))} </div> {isPending && <div className="loading">加载中...</div>} <div className="tab-content"> <Suspense fallback={<div>加载中...</div>}> {tab === 'home' && <Home />} {tab === 'products' && <Products />} {tab === 'about' && <About />} {tab === 'contact' && <Contact />} </Suspense> </div> </div> ); }

九、小结

要点说明
useTransition标记非紧急更新,保持 UI 响应
useDeferredValue延迟更新值,用于外部传入
适用场景搜索过滤、表格排序、路由切换
用户体验提供 isPending 视觉反馈

核心要点:

  • useTransition 和 useDeferredValue 是并发渲染的核心 API
  • 将昂贵的更新标记为低优先级
  • 始终提供视觉反馈(isPending)
  • 不要过度使用,简单更新不需要

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 2:19:56

无法在Windows 10、win11上下载或被拦截

ttkefu无法在Windows 10上下载的原因主要是因为Windows Defender的实时防护功能阻止了安装。?具体来说&#xff0c;当Windows 10的杀毒和威胁防护功能开启时&#xff0c;它会阻止未经认证的软件安装&#xff0c;尤其是那些不在白名单中的应用。ttkefu在线沟通软件可能没有被识…

作者头像 李华
网站建设 2026/6/9 2:18:01

Panda3D:开源 3D 游戏引擎,Python 与 C++ 双语言支持

文章目录Panda3D&#xff1a;开源 3D 游戏引擎&#xff0c;Python 与 C 双语言支持Panda3D&#xff1a;开源 3D 游戏引擎&#xff0c;Python 与 C 双语言支持 Panda3D 是一款开源的 3D 渲染和游戏开发框架&#xff0c;支持 Python 和 C 两种编程语言&#xff0c;目前在 GitHub…

作者头像 李华
网站建设 2026/6/9 2:17:54

让两个 Agent 互相挑错:一个写、一个审,把瞎编率压下去

单个 Agent 有个老毛病&#xff1a;它对自己的错误没有感知&#xff0c;瞎编了也一脸自信。我做一个要求比较严的问答场景时&#xff0c;被它一本正经的错误答案坑过几次。 后来试了个法子&#xff1a;让两个 Agent 分工&#xff0c;一个负责答、一个专门挑错&#xff0c;准确…

作者头像 李华