React 18使用useTransition()时大列表输入仍卡顿的原因
useTransition() 未生效导致输入卡顿的核心原因 useTransition 的作用是标记低优先级状态更新,让输入、点击这类紧急交互的更新优先响应,但它不会减少实际需要执行的渲染工作量,也不会自动跳过非必要的组件重渲染,你的代码存在几个关键问题,导致优先级调度完全没发挥作用:
- 未对大体积列表组件做记忆化隔离:输入触发
setNeedle这个高优先级更新时,App组件会重渲染,由于startTransition触发时isPending会同步更新为高优先级状态,PeopleList组件接收到的isLoading属性发生变化,会跟着一起重渲染,直接同步执行2万条列表项的遍历、颜色计算、React元素生成逻辑,这个耗时约0.5秒的长任务直接阻塞主线程,输入框自然会卡顿。 - 存在不必要的重复计算:每次渲染列表项时,都会对每条名称执行一次
toLowerCase(),2万条数据每次渲染都会重复执行这个操作,额外增加了计算耗时。 - 全量渲染2万条DOM节点:
useTransition只能调整更新的优先级,不会拆分或减少实际的DOM渲染、diff工作量。2万条节点全量渲染的成本本身就很高,哪怕优先级调度生效,长渲染任务依然会阻塞交互。 - 容易忽略的前提:如果项目入口没有使用React 18的
createRootAPI,而是沿用旧版的ReactDOM.render,所有并发特性(包括useTransition)都不会生效,行为和React 17完全一致。
对应优化方案
- 确认入口使用
createRoot挂载应用,开启React 18并发能力:
import { createRoot } from 'react-dom/client'; const root = createRoot(document.getElementById('root')); root.render(<App />);
- 用
React.memo包裹PeopleList组件,仅当people/highlight/isLoading三个属性实际变化时才重渲染列表,隔离输入框的高优先级重渲染逻辑,避免输入时带着大列表一起重跑。 - 提前预处理名称数据,导入JSON后一次性把所有名称转成小写,避免渲染时重复计算:
const processedPeople = people.map(name => ({ original: name, lowerCase: name.toLowerCase() }));
后续匹配时直接用预计算的lowerCase字段做includes判断即可。
- 针对2万条规模的列表,必须搭配虚拟列表方案,只渲染视口内可见的数十条条目,把实际渲染的DOM节点数量从2万降到几十,从根源上降低渲染开销。
useTransition只能在渲染工作量本身可控的前提下优化交互响应顺序,无法替代虚拟列表解决大列表渲染的性能问题。
内容的提问来源于stack exchange,提问作者Robo Robok
相关产品推荐
相关产品推荐

