当前位置: 首页 > 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.jsqmd.com/news/978395/

相关文章:

  • WPS表格进阶玩法:巧用‘文本转表格’功能,一键处理调查问卷和导出数据
  • 不止于稀疏点云:用OpenMVG 2.0完成SFM后,如何无缝衔接OpenMVS进行稠密重建?
  • 别再手动对齐了!用Word/WPS的‘文本转表格’功能,5分钟搞定杂乱数据整理
  • pdfplumber:Python PDF 解析与表格提取利器
  • 简单C++
  • 其他推荐 - 本地品牌推荐
  • 光猫‘死前’信号揭秘:DyingGasp电路在PON网络中的实战应用与故障排查指南
  • 【STM32】配置vscode+C工具链+Cortex-Debug开发环境,IC:STM32F411CEU6
  • 双组份背胶选购指南,兴佰诚值得选吗 - mypinpai
  • 从水箱报警到花盆浇水:用窗口比较器LM393DIY一个超实用的水位监控器
  • MyComputerManager:基于WPF的Windows注册表管理系统架构深度解析
  • 多标签表单与文件上传的完美结合
  • 从OFDM仿真到性能对比:深入理解LMMSE与LS信道估计的MATLAB实战(含信噪比影响分析)
  • 小型化免提设备中的回声消除与双麦阵列设计:以A-29模块为例的技术解析
  • 2026会计专业学数据分析的价值
  • 2026年6月目前评价好的排污泵源头厂家找哪家,不锈钢无负压供水设备/灌溉泵/一体化泵站,排污泵制造商哪家强 - 品牌推荐师
  • 【问题解决】xftp工具无法连接Windows问题解决
  • 广州电脑键盘故障维修:广州电脑维修硬件故障解决、广州电脑维修软件故障修复、广州电脑维修键盘故障、广州蓝屏电脑维修选择指南 - 优质品牌商家
  • 2026年兴佰诚瓷砖胶推荐有哪些? - mypinpai
  • 保姆级教程:手把手复现CVPR 2021 CenterPoint,从环境配置到模型训练全流程
  • 从零复现PointPillars:基于PyTorch和KITTI数据集的保姆级训练与部署指南
  • 2026年AI广告推广选购指南,南通摘星推荐 - mypinpai
  • 基于Stackelberg博弈的分散式库存模型
  • 计算机毕业设计之黄河文化资源管理系统
  • D49: 团队协作中的信息保护管理
  • 618流量内卷加剧,好客搜GEO优化,助力商家低成本抢占精准客源
  • 从数据库主键到文件命名:UUID的五个版本在实际开发中的‘避坑’指南
  • 2026年6月青岛配镜门店最新排行 基于专业度与口碑实测 - 奔跑123
  • 如何用HunterPie智能覆盖插件让《怪物猎人:世界》的狩猎体验提升300%?
  • 入境就医服务公司上海哪家专业