当前位置: 首页 > news >正文

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)
  • 不要过度使用,简单更新不需要

http://www.gsyq.cn/news/1490121.html

相关文章:

  • 不止于稀疏点云:用OpenMVG 2.0完成SFM后,如何无缝衔接OpenMVS进行稠密重建?
  • 双组份背胶选购指南,兴佰诚值得选吗 - mypinpai
  • 从OFDM仿真到性能对比:深入理解LMMSE与LS信道估计的MATLAB实战(含信噪比影响分析)
  • 小型化免提设备中的回声消除与双麦阵列设计:以A-29模块为例的技术解析
  • 2026会计专业学数据分析的价值
  • 【问题解决】xftp工具无法连接Windows问题解决
  • 2026年6月目前评价好的排污泵源头厂家找哪家,不锈钢无负压供水设备/灌溉泵/一体化泵站,排污泵制造商哪家强 - 品牌推荐师
  • 保姆级教程:手把手复现CVPR 2021 CenterPoint,从环境配置到模型训练全流程
  • 618流量内卷加剧,好客搜GEO优化,助力商家低成本抢占精准客源
  • 从数据库主键到文件命名:UUID的五个版本在实际开发中的‘避坑’指南
  • 计算机毕业设计之黄河文化资源管理系统
  • 如何用HunterPie智能覆盖插件让《怪物猎人:世界》的狩猎体验提升300%?
  • 2026年AI广告推广选购指南,南通摘星推荐 - mypinpai
  • STM32程序防抄攻略:手把手教你用ST-LINK Utility设置读写保护(含解除方法)
  • 突破网盘限速的技术革新:直链下载助手深度解析
  • 让两个 Agent 互相挑错:一个写、一个审,把瞎编率压下去
  • 告别安装报错!保姆级Quartus II 13.1安装与驱动配置全攻略(附正点原子资源)
  • 【MySQL高阶】25.通用临时表空间
  • 鸿蒙PC上跑 simdjson?AtomCode + Skills 说:这不是移植,这是“粘贴即用“
  • 2026年膏状瓷砖背胶技术选型指南及品牌参考:家装瓷砖胶、屋顶防水材料、强力瓷砖背胶、强力瓷砖胶、新型防水材料选择指南 - 优质品牌商家
  • Vivado调试之痛:遇到‘debug hub core not detected’?别慌,这份Ibert核识别失败排查清单请收好
  • 云南土工格栅拉力越大越好吗?
  • 哈氏合金无缝管哪个品牌好? - 工业设备
  • 手把手教你用Simulink搭建异步电机矢量控制模型(附PI参数调试心得)
  • 试用zeroclaw
  • 抖音大模型二面:讲讲 Transformer 架构的基本原理?Encoder 和 Decoder 是什么?
  • 3步解锁开源项目扩展技能:为小说下载器添加新网站支持
  • 用PyQt5做GUI?先花5分钟搞定PyCharm插件化开发环境(附国内镜像源)
  • 深聊 CPU 用聚酯多元醇的口碑品牌? - mypinpai
  • SOLIDWORKS转CAD字体终极指南:TrueType还是SHX?选错可能导致图纸报废!