尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

React 18 useTransition 实战:过滤大列表、切 Tab 时保持输入不卡顿

React 18 useTransition 实战:过滤大列表、切 Tab 时保持输入不卡顿
📅 发布时间:2026/8/1 22:31:21

React 18 useTransition 实战:过滤大列表、切 Tab 时保持输入不卡顿

你做过一个带搜索框的大列表:输入框下面是几千条数据,每敲一个字符就实时过滤。用户打字时明显感觉输入框「粘手」——字母延迟半拍才出现,退格也卡。你检查过没有多余请求、没有死循环,罪魁祸首其实是:每次输入都触发一次几千条的重渲染,而这个重渲染和「更新输入框」抢占同一优先级。React 18 的useTransition就是为这种场景造的,它能告诉 React「这次更新不急,别挡着用户打字」。

问题复现:输入和过滤挤在一起

先看没优化的版本,一个 5000 项列表的实时过滤:

import { useState } from "react"; const ALL = Array.from({ length: 5000 }, (_, i) => `Item ${i}`); function SearchList() { const [query, setQuery] = useState(""); const [list, setList] = useState(ALL); function handleChange(e) { const value = e.target.value; setQuery(value); // 更新输入框 setList(ALL.filter((x) => x.includes(value))); // 过滤 5000 项,重渲染很重 } return ( <> <input value={query} onChange={handleChange} /> <ul> {list.map((x) => <li key={x}>{x}</li>)} </ul> </> ); }

这里两个setState是同一优先级,React 会在同一次渲染里既更新输入框、又渲染 5000 个<li>。列表渲染慢,输入框的更新就被它拖着一起慢,于是打字卡顿。

用 useTransition 把过滤标记为「非紧急」

useTransition返回[isPending, startTransition]。把「重的、可以晚一点」的更新包进startTransition,React 就会优先处理紧急更新(输入框),把 transition 更新放到后面、且可被后续输入打断:

import { useState, useTransition } from "react"; const ALL = Array.from({ length: 5000 }, (_, i) => `Item ${i}`); function SearchList() { const [query, setQuery] = useState(""); const [list, setList] = useState(ALL); const [isPending, startTransition] = useTransition(); function handleChange(e) { const value = e.target.value; setQuery(value); // 紧急更新:输入框必须立刻响应 // 非紧急更新:过滤结果晚一点没关系,还能被下一次输入打断 startTransition(() => { setList(ALL.filter((x) => x.includes(value))); }); } return ( <> <input value={query} onChange={handleChange} /> {isPending && <span>过滤中…</span>} <ul style={{ opacity: isPending ? 0.6 : 1 }}> {list.map((x) => <li key={x}>{x}</li>)} </ul> </> ); }

现在打字始终跟手:setQuery是紧急的,立刻更新;setList被标记为 transition,React 会在空隙里去算,而且如果你在它算完前又敲了一个字符,上一次的过滤会被直接丢弃,不做无用功。isPending让你在过渡期间给个「过滤中」的视觉反馈,避免界面看起来卡住。

关键规则:startTransition 里只能放 set,不能放读到的值

一个高频踩坑:很多人想把输入值本身也放进 transition,结果输入框反而更卡了。记住驱动输入框的那个 state 必须留在 transition 外面:

// ❌ 错误:把 setQuery 也包进去,输入框更新被降级,打字更卡 startTransition(() => { setQuery(value); setList(ALL.filter((x) => x.includes(value))); }); // ✅ 正确:输入框紧急更新在外,重活在里面 setQuery(value); startTransition(() => { setList(ALL.filter((x) => x.includes(value))); });

还有一点:startTransition的回调必须是同步的。如果你在里面写await fetch(...)再setState,那个setState已经脱离了 transition 上下文,不会被当作非紧急更新。异步数据场景要用use或数据请求库配合 Suspense,而不是把 await 塞进 startTransition。

切 Tab 场景:同样适用

useTransition不只用于搜索。任何「点一下会触发重渲染,但你希望点击本身立刻有反馈」的场景都合适,典型的是切换 Tab 加载不同的重内容:

function Tabs() { const [tab, setTab] = useState("home"); const [isPending, startTransition] = useTransition(); function selectTab(next) { // 让 Tab 的高亮切换立刻发生,重内容的渲染延后 startTransition(() => setTab(next)); } return ( <> <nav> {["home", "posts", "charts"].map((t) => ( <button key={t} onClick={() => selectTab(t)} style={{ fontWeight: t === tab ? "bold" : "normal" }} > {t} </button> ))} </nav> {isPending && <p>加载中…</p>} <TabContent tab={tab} /> {/* 假设 charts 这个 tab 渲染很重 */} </> ); }

点击charts时,即使它的内容渲染要 200ms,isPending会立刻变true给出反馈,用户不会觉得点击「没反应」,而旧的 Tab 内容还留在屏幕上直到新内容准备好。

useTransition vs useDeferredValue:怎么选

两者都能解决「重更新拖累交互」,区别在于你控制的是「触发」还是「值」:

  • useTransition:你能改动那次setState的代码时用它,直接把 set 包进startTransition。额外送你一个isPending。
  • useDeferredValue:你拿到的是别人传进来的 prop / 已有 state,改不了它的 set 调用时用它,const deferred = useDeferredValue(value),让派生渲染用延迟值。

一句话:能碰 setState 就用useTransition,只能碰值就用useDeferredValue。

小结

  • 卡顿的根因常是「紧急更新(输入/点击)」和「重更新(大列表渲染)」挤在同一优先级。
  • useTransition把重更新包进startTransition,让 React 优先响应交互,过渡更新可被后续操作打断,不做无用功。
  • 驱动输入框的setState必须留在 transition外面,否则越优化越卡;startTransition回调要同步,别塞await。
  • 用isPending给过渡期一个视觉反馈,界面不会看起来「死了」。
  • 一句话记忆:急的放外面,重的进startTransition;改得了 set 用useTransition,只拿得到值用useDeferredValue。

相关新闻

  • 【单片机毕业设计】基于 51 单片机的手动自动双模式环境治理设备设计 基于传感器阵列的室内空气质量智能监测平台设计(017801)
  • 跨平台流媒体下载利器:N_m3u8DL-RE如何破解DASH/HLS/MSS下载难题
  • Google Workspace CLI高级教程:Drive文件批量操作与共享管理

最新新闻

  • Grok 4.5推理能力深度解析:适用问题、评测指标与能力边界
  • 番禺叉车培训哪家强?广州铭智教育用实力说话
  • LaserGRBL:5个理由让这款开源激光雕刻软件成为你的创作利器
  • Web3.0新测试方案
  • PyTorch深度学习环境搭建与核心概念实战指南
  • Flask与Vue.js构建全国面食文化交流平台技术解析

日新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号