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

【React】 Hooks useTransition 解析与性能优化实践

1.背景

  1. useTransition 是 React 18 引入的一个并发模式下的 Hook,用于区分紧急和非紧急的状态更新,提升应用的响应性和用户体验;
  2. 它可以管理 UI 中的过渡状态,特别是在处理长时间运行的状态更新时。
  3. 它允许你将某些更新标记为“过渡”状态,这样 React 可以优先处理更重要的更新,比如用户输入,同时延迟处理过渡更新。
  4. 以下是其核心要点、使用场景和注意事项:

2.核心功能

  1. 标记过渡状态:将非紧急的状态更新标记为“过渡”(Transition),允许 React 延迟处理这些更新,优先处理高优先级任务(如用户交互)。
  2. 保持 UI 响应:在状态转换期间,组件仍能保持响应,避免界面卡顿。
  3. 提供加载状态:通过 isPending 指示过渡任务是否正在进行,可配合加载指示器(如 Spinner)提升用户体验。

3.基本用法

3.1 eg:

import { useTransition } from 'react';function MyComponent() {const [isPending, startTransition] = useTransition();const [count, setCount] = useState(0);const handleClick = () => {startTransition(() => {setCount(c => c + 1); // 标记为过渡更新});};return (<div>{isPending && <Spinner />} {/* 显示加载指示器 */}<button onClick={handleClick}>Increment</button><p>Count: {count}</p></div>);
}

3.1 参数

useTransition 不需要任何参数

3.2 返回值

useTransition 返回一个数组 包含两个元素[isPending, startTransition]

  1. isPending:布尔值,表示过渡任务是否正在进行。
  2. startTransition:函数,用于包裹低优先级的状态更新。

4、使用场景

4.1 大量数据渲染

例如,过滤或排序大型列表时,避免界面卡顿。

function DataGrid() {const [data, setData] = useState([]);const [isPending, startTransition] = useTransition();const [filter, setFilter] = useState('');const handleFilterChange = (newFilter) => {setFilter(newFilter);startTransition(() => {const filteredData = processLargeDataSet(newFilter);setData(filteredData);});};return (<div><input value={filter} onChange={e => handleFilterChange(e.target.value)} />{isPending ? <LoadingGrid /> : <VirtualizedGrid data={data} />}</div>);
}

4.2 路由切换

预加载下一页数据时保持当前页响应。

function App() {const [isPending, startTransition] = useTransition();const [currentPage, setCurrentPage] = useState('home');const navigate = (page) => {startTransition(() => {setCurrentPage(page);});};return (<div><Navigation onNavigate={navigate} />{isPending ? <PageTransitionSpinner /> : <Page name={currentPage} />}</div>);
}

4.3 表单验证

实时响应用户输入,同时延迟更新验证结果。

function ComplexForm() {const [formData, setFormData] = useState({});const [errors, setErrors] = useState({});const [isPending, startTransition] = useTransition();const handleChange = (e) => {const { name, value } = e.target;setFormData(prev => ({ ...prev, [name]: value }));startTransition(() => {const validationErrors = validateFormField(name, value);setErrors(prev => ({ ...prev, [name]: validationErrors }));});};return (<form><input name="email" onChange={handleChange} value={formData.email || ''} />{isPending ? <ValidatingIndicator /> : (errors.email && <ErrorMessage error={errors.email} />)}</form>);
}

4.4 搜索或筛选功能

实时响应用户输入,同时延迟更新结果展示。

function SearchResults() {const [query, setQuery] = useState('');const [results, setResults] = useState([]);const [isPending, startTransition] = useTransition();const handleSearch = (e) => {setQuery(e.target.value);startTransition(() => {const searchResults = performSearch(e.target.value);setResults(searchResults);});};return (<div><input value={query} onChange={handleSearch} />{isPending ? <div>Loading...</div> : (<ul>{results.map(result => (<li key={result.id}>{result.title}</li>))}</ul>)}</div>);
}

5. 场景模拟

创建了一个简单的输入框和一个列表,用于展示基于输入关键词的结果。

mockjs文档地址:https://github.com/nuysoft/Mock/wiki/Getting-Started

5.1 安装使用到的插件

pnpm add mockjs antd 
pnpm add -D @types/mockjs @type/node

package.json

{...."dependencies": {"antd": "^5.24.9","mockjs": "^1.1.0","react": "^19.0.0","react-dom": "^19.0.0"},"devDependencies": {"@types/mockjs": "^1.0.10","@types/node": "^22.15.3","@types/react": "^19.1.2",.....}
}

5.2 编写 vite.config.ts

结合 vite插件实现一个api, 这个api可以帮助我们模拟数据。

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import type { Plugin } from 'vite'
import mockjs from 'mockjs'
// 使用 URL 模块解析 URL
import url from 'node:url'/*** 创建一个 Vite Mock 服务器插件* @returns Vite 插件实例*/
const viteMockServer = (): Plugin => {return {// 插件名称name: 'vite-plugin-mock',// 配置开发服务器configureServer(server) {// 添加中间件处理 mock 请求server.middlewares.use('/api/mock/list', (req, res) => {// 解析请求 URL 中的查询参数const parseurl = url.parse(req.originalUrl ?? '', true).query;// 设置响应头为 JSON 格式res.setHeader('Content-Type', 'application/json');// 使用 mockjs 生成模拟数据const data = mockjs.mock({'list|2000': [{id: '@guid',// name: '@cword(2, 4)',name: parseurl.key,age: '@integer(18, 30)',address: '@county(true)',},],});// 将数据转换为 JSON 字符串并发送响应res.end(JSON.stringify(data));})}}
}// https://vite.dev/config/
export default defineConfig({plugins: [react(), viteMockServer()],
})

编写完成访问我们的接口 http://localhost:5174/api/list?keyWord=xx 5174为默认端口,可以自行更改,返回数据如下

{"list": [{"id": "DCe1D11e-8D24-31e6-fE76-5AbAA6cf7E6F","name": "a","age": 25,"address": "西藏自治区 日喀则地区 仲巴县"},{"id": "EAb5ffb6-b43B-93cC-cDfb-18A7Ce15BFda","name": "a","age": 21,"address": "安徽省 马鞍山市 花山区"}.......]
}

5.3 业务组件编写

App.tsx

  1. 输入框和状态管理 使用 useState Hook 管理输入框的值和结果列表。 每次输入框的内容变化时,handleInputChange 函数会被触发,它会获取用户输入的值,并进行 API 请求。
  2. API 请求 在 handleInputChange 中,输入的值会作为查询参数发送到 /api/list API。API 返回的数据用于更新结果列表。 为了优化用户体验,我们将结果更新放在 startTransition 函数中,这样 React 可以在处理更新时保持输入框的响应性。
  3. 使用 useTransition useTransition 返回一个布尔值 isPending,指示过渡任务是否仍在进行中。 当用户输入时,如果正在加载数据,我们会显示一个简单的“loading…”提示,以告知用户当前操作仍在进行。
  4. 列表渲染 使用 List 组件展示返回的结果,列表项显示每个结果的 name 和 address。
// 使用 React 和 Ant Design 组件实现一个模拟带搜索功能的列表
import { Input, List, Spin } from 'antd'
import { useState, useTransition, useCallback } from 'react'interface ListItem {id: string;name: string;address: string;age: number;
}function App() {const [list, setList] = useState<ListItem[]>([]);const [inputValue, setInputValue] = useState('');const [isPending, startTransition] = useTransition();// 处理输入框变化,使用 useCallback 优化性能const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {const value = e.target.value;setInputValue(value);// 发起请求获取数据fetch(`/api/mock/list?key=${value}`).then((res) => res.json()).then((res) => {// 使用 startTransition 包裹状态更新,优化大数据渲染性能startTransition(() => {setList(res.list)});}).catch(err => {console.error('获取数据失败:', err);});}, []);return (<><Input placeholder="请输入搜索关键词"value={inputValue} onChange={handleInputChange} />{isPending ? (<Spin tip="加载中..." />) : (<ListdataSource={list}renderItem={(item) => (<List.Item>{item.address}</List.Item>)}/>)}</>);
}export default App;

5.4 测试使用

为了更好的测试结果可以在性能中降级 cpu 渲染速度

在这里插入图片描述

6. 注意事项

startTransition必须是同步的

错误做法

startTransition(() => {// ❌ 在调用 startTransition 后更新状态setTimeout(() => {setPage('/about');}, 1000);
}); 

正确做法

setTimeout(() => {startTransition(() => {// ✅ 在调用 startTransition 中更新状态setPage('/about');});
}, 1000);

async await 错误做法

startTransition(async () => {await someAsyncFunction();// ❌ 在调用 startTransition 后更新状态setPage('/about');
});

正确做法

await someAsyncFunction();
startTransition(() => {// ✅ 在调用 startTransition 中更新状态setPage('/about');
});

7. 原理剖析

useTransition 的核心原理是将一部分状态更新处理为低优先级任务,这样可以将关键的高优先级任务先执行,而低优先级的过渡更新则会稍微延迟处理。这在渲染大量数据、进行复杂运算或处理长时间任务时特别有效。React 通过调度机制来管理优先级:

  1. 高优先级更新:直接影响用户体验的任务,比如表单输入、按钮点击等。
  2. 低优先级更新:相对不影响交互的过渡性任务,比如大量数据渲染、动画等,这些任务可以延迟执行。
  +-----------------------+|         App           ||                       ||  +--------------+     ||  |    Input     |     ||  +--------------+     ||                       ||  +--------------+     ||  |   Display    |     ||  +--------------+     |+-----------------------+用户输入|v[高优先级更新] ---> [调度器] ---> [React 更新组件]|+---> [低优先级过渡更新] --> [调度器] --> [等待处理]

8.扩展

useTransition 与防抖的区别?

seTransition与 防抖在前端开发中都是用于优化性能的手段,但它们的核心原理、应用场景和实现方式存在显著区别,以下是详细对比:

核心原理对比

  • useTransition
    • 基于React的并发模式(Concurrent Features),通过将状态更新标记为“过渡”(Transition)实现。
    • 允许React在后台处理低优先级任务,优先响应高优先级操作(如用户输入),从而保持界面流畅。
    • 更新过程可中断,避免长时间阻塞主线程,提升用户体验。
  • 防抖(Debounce)
    • 延迟函数执行,直到事件触发后的一段时间内没有再次触发。
    • 适用于需要等待用户停止操作后再执行的场景(如搜索输入、表单验证)。

应用场景对比

  • useTransition适用场景
    • 大数据列表过滤:当用户输入搜索内容时,通过useTransition将筛选任务标记为低优先级,避免阻塞输入框的实时更新,保持输入流畅性。
    • 复杂UI更新:在渲染需要消耗大量时间的页面时,使用useTransition优化视图切换体验。
    • 表单提交与筛选器切换:需要延迟渲染的操作(如异步数据加载)中,useTransition可确保用户交互的即时响应。
  • 防抖适用场景
    • 搜索框输入检测:等待用户停止输入后再执行搜索,减少无效请求。
    • 手机号和邮箱验证:在用户停止输入后验证格式,避免实时验证的性能开销。
    • 窗口大小变化后的重新渲染:在窗口调整结束后执行布局计算,避免频繁重绘。

优缺点对比

  • useTransition优点
    • 更新协调过程可中断,避免长时间阻塞主线程。
    • 用户操作可及时得到响应,提升交互体验。
    • 不需要开发者手动配置时间间隔,React自动优化。
  • useTransition缺点
    • 仅适用于React 18及以上版本,且需配合并发模式使用。
    • 对于简单场景,可能引入不必要的复杂性。
  • 防抖优点
    • 可确保在用户停止操作后执行函数,减少无效请求。
    • 实现简单,适用于等待用户输入的场景。
  • 防抖缺点
    • 可能导致用户输入长时间得不到响应。
    • 无法处理需要即时反馈的操作。

相关文章:

  • SecureCRT设置自定义快捷键
  • 【Java项目脚手架系列】第一篇:Maven基础项目脚手架
  • 软考-软件设计师中级备考 9、存储管理
  • RR(Repeatable Read)级别如何防止幻读
  • 健康养生指南:科学守护身心
  • go实现循环链表
  • 位图的实现和拓展
  • Dubbo(94)如何在金融系统中应用Dubbo?
  • 【翻译、转载】使用 LLM 构建 MCP
  • 健康生活,从点滴养生开始
  • mysql-内置函数,复合查询和内外连接
  • 【React Hooks原理 - useCallback、useMemo】
  • 多语言笔记系列:Polyglot Notebooks 混合使用多语言并共享变量
  • 复刻低成本机械臂 SO-ARM100 标定篇
  • Vue常用优化
  • jeecg查询指定时间
  • 系统架构设计师:设计模式——行为设计模式
  • 【Touching China】2007-2011
  • WordPress不支持中文TAG标签出现404的解决方法
  • ES6入门---第三单元 模块三:async、await
  • 贵州黔西市游船倾覆事故致9人死亡1人失联
  • 国防部新闻发言人就日本民用飞机侵闯中国钓鱼岛领空答问
  • 五一假期首日,上海外滩客流超55万人次
  • 商务部新闻发言人就中美经贸对话磋商情况答记者问
  • 2025五一档首日电影票房破亿
  • 奥斯卡新规:评委必须看完影片再投票;网友:以前不是啊?