【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及以上版本,且需配合并发模式使用。
    • 对于简单场景,可能引入不必要的复杂性。
  • 防抖优点
    • 可确保在用户停止操作后执行函数,减少无效请求。
    • 实现简单,适用于等待用户输入的场景。
  • 防抖缺点
    • 可能导致用户输入长时间得不到响应。
    • 无法处理需要即时反馈的操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/81694.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蘑菇管理——AI与思维模型【94】

一、定义 蘑菇管理思维模型是一种形象地描述组织对待新员工或初入职场者的管理方式及相关现象的思维模型。它将新员工或初入职场者比作蘑菇&#xff0c;这些人在初期往往被置于阴暗的角落&#xff08;不受重视的部门&#xff0c;或打杂跑腿的工作&#xff09;&#xff0c;浇上…

c++STL——set和map的使用

文章目录 set和map的使用set系列声名和定义默认成员函数迭代器set的增删查lower_bound和upper_boundInsert接口pair类 对于查找的另一种使用 set和multiset的区别 map系列声名和定义pair类的进一步介绍默认成员函数map的增删查map的数据修改map和multimap的差异 set和map的使用…

什么是DGI数据治理框架?

DGI数据治理框架是由数据治理研究所&#xff08;Data Governance Institute, DGI&#xff09;提出的一套系统性方法论&#xff0c;旨在帮助企业或组织建立有效的数据治理体系&#xff0c;确保数据资产的高质量管理、合规使用和价值释放。以下是关于DGI数据治理框架的核心内容&a…

chrome 浏览器怎么不自动提示是否翻译网站

每次访问外国语网页都会弹出这个对话框&#xff0c;很是麻烦&#xff0c;每次都得手动关闭一下。 不让他弹出来方法&#xff1a; 设置》语言》首选语言》添加语言&#xff0c;搜索英语添加上 如果需要使用翻译&#xff0c;就点击三个点&#xff0c;然后选择翻译

LeetCode 热题 100 54. 螺旋矩阵

LeetCode 热题 100 | 54. 螺旋矩阵 大家好&#xff0c;今天我们来解决一道经典的算法题——螺旋矩阵。这道题在LeetCode上被标记为中等难度&#xff0c;要求我们按照顺时针螺旋顺序返回矩阵中的所有元素。下面我将详细讲解解题思路&#xff0c;并附上Python代码实现。 问题描述…

生成式AI将重塑的未来工作

在人类文明的长河中,技术革命始终是推动社会进步的核心动力。从蒸汽机的轰鸣到互联网的浪潮,每一次技术跃迁都在重塑着人类的工作方式与生存形态。而今,生成式人工智能(Generative AI)的崛起,正以超越以往任何时代的速度与深度,叩响未来工作范式变革的大门。这场变革并非…

【2025软考高级架构师】——2024年05月份真题与解析

摘要 本文内容是关于2025年软考高级架构师考试的相关资料&#xff0c;包含2024年05月份真题与解析。其中涉及体系结构演化的步骤、OSI协议中能提供安全服务的层次、数据库设计阶段中进行关系反规范化的环节等知识点&#xff0c;还提及了软考高级架构师考试的多个模块&#xff…

KAG:通过知识增强生成提升专业领域的大型语言模型(三)

目录 摘要 Abstract 1 Schema 2 Prompt 3 KAG-Builder 3.1 reader 3.2 splitter 3.3 extractor 3.4 vectorizer 3.5 writer 3.6 可选组件 4 示例 总结 摘要 本周深入学习了 KAG 项目中的 Schema、Prompt 以及 KAG-Builder 相关代码知识&#xff0c;涵盖了其定义、…

Gitea windows服务注册,服务启动、停止、重启脚本

修改配置文件 查看COMPUTERNAME echo %COMPUTERNAME%进入配置文件D:\gitea\custom\conf\app.ini&#xff0c;将 Gitea 设置为以本地系统用户运行 如果结果是 USER-PC&#xff0c;那么 RUN_USER USER-PC$ RUN_USER COMPUTERNAME$SQLite3 PATH配置&#xff0c;更改为包含完整…

矿泉水瓶的绘制

1.制作中心矩形&#xff0c;大小为60&#xff0c;注意设置矩形的两条边相等 2.点击拉伸&#xff0c;高度为150mm 3.使用圆角命令&#xff0c;点击连接到开始面&#xff0c;同时选中4条边&#xff0c;进行圆角转化&#xff0c;圆角大小为10mm&#xff0c;点击多半径圆角&#xf…

【程序+论文】大规模新能源并网下的火电机组深度调峰经济调度

目录 1 主要内容 讲解重点 2 讲解视频及代码 1 主要内容 该视频为《大规模新能源并网下的火电机组深度调峰经济调度》代码讲解内容&#xff0c;该程序有完全对照的论文&#xff0c;以改进IEEE30节点作为研究对象&#xff0c;系统包括5个火电机组和2个新能源机组&#xff0c;…

​​工业机器人智能编程:从示教器到AI自主决策​​

工业机器人智能编程:从示教器到AI自主决策 引言 工业机器人作为智能制造的核心装备,其编程方式正经历革命性变革。传统示教器编程效率低下,平均每个路径点需要30秒人工示教,而复杂轨迹编程可能耗时数周。随着AI技术的发展,工业机器人编程正朝着"所见即所得"的…

n8n 构建一个 ReAct AI Agent 示例

n8n 构建一个 ReAct AI Agent 示例 0. 引言1. 详细步骤创建一个 "When Executed by Another Workflow"创建一个 "Edit Fields (Set)"再创建一个 "Edit Fields (Set)"创建一个 HTTP Request创建一个 If 节点在 true 分支创建一个 "Edit Fiel…

Monorepo项目多项目一次性启动工具对比与实践

Monorepo项目多项目一次性启动工具对比与实践 在现代软件开发中&#xff0c;Monorepo&#xff08;单一仓库&#xff09;模式越来越受到开发者的青睐。Monorepo将多个相关的项目或包集中在一个仓库中进行管理&#xff0c;方便依赖共享、代码复用和统一发布。在Monorepo项目开发…

笔记整理六----OSPF协议

OSPF 动态路由的分类&#xff1a; 1.基于网络范围进行划分--将网络本身划分为一个个AS&#xff08;自治系统---方便管理和维护&#xff09; 内部网关协议---负责AS内部用户之间互相访问使用的协议 IGP--RIP EIGRP ISIS OSPF 外部网关协议--负责AS之间&#xff08;整个互联网&…

网络编程,使用select()进行简单服务端与客户端通信

这里在Ubuntu环境下演示 一般流程 服务端常用函数&#xff1a; socket()&#xff1a;创建一个新的套接字。bind()&#xff1a;将套接字与特定的IP地址和端口绑定。listen()&#xff1a;使套接字开始监听传入的连接请求。accept()&#xff1a;接受一个传入的连接请求&#xff…

智能决策支持系统的基本概念与理论体系

决策支持系统是管理科学的一个分支&#xff0c;原本与人工智能属于不同的学科范畴&#xff0c;但自20世纪80年代以来&#xff0c;由于专家系统在许多方面取得了成功&#xff0c;于是人们开始考虑把人工智能技术用于计算机管理中来。在用计算机所进行的各种管理中&#xff0c;如…

驱动开发系列55 - Linux Graphics QXL显卡驱动代码分析(二)显存管理

一:概述 前面介绍了当内核检测到匹配的PCI设备后,会调用 qxl_pci_probe 初始化设备,其中会调用qxl_device_init 来初始化设备,为QXL设备进行内存映射,资源分配,环形缓冲区初始化,IRQ注册等操作,本文展开说说这些细节,以及介绍下QXL的显存管理。 二:QXL设备初始化细节…

洛谷 P1495:【模板】中国剩余定理(CRT)/ 曹冲养猪

【题目来源】 https://www.luogu.com.cn/problem/P1495 https://www.acwing.com/problem/content/225/ 【题目描述】 自从曹冲搞定了大象以后&#xff0c;曹操就开始捉摸让儿子干些事业&#xff0c;于是派他到中原养猪场养猪。可是曹冲满不高兴&#xff0c;于是在工作中马马虎…

配置和使用持久卷

配置和使用持久卷 文章目录 配置和使用持久卷[toc]一、PV与PVC的持久化存储机制二、PV和PVC的生命周期三、创建基于NFS的PV1.准备NFS共享目录2.创建PV 四、基于PVC使用PV1.创建PVC2.使用PVC 五、基于StorageClass实现动态卷制备1.获取NFS服务器的连接信息2.获取nfs-subdir-exte…