React组件渲染与重渲染
React 组件什么时候渲染、为什么重渲染,以及如何正确理解重渲染与性能问题的关系。
[!info] related notes
- 所属 MOC: React MOC
- 上位主题: react
- 相关概念: react-use-state, react-use-memo, react-use-callback, react-use-ref, react-hooks
- 延伸阅读: react-performance-overview
React组件渲染与重渲染
理解 React,不能只停留在“状态变了页面就变了”,还要理解组件什么时候会重新执行,以及这和性能问题到底是什么关系。
一句话定义
React 组件重渲染,本质上是组件函数重新执行,得到新的 UI 描述,再由 React 比较差异并更新必要部分。
常见触发来源
- 组件自己的 state 变化
- 父组件重渲染并传入新的 props
- context 值变化
每次重渲染时到底发生了什么
- 组件函数重新执行
- Hook 按顺序重新调用
- React 从对应槽位取出旧状态
- 得到新的 JSX 结果
- React 比较前后差异
- 只提交必要的 DOM 更新
- 需要时再运行 effect cleanup 和新的 effect
这也是为什么 React 组件里的普通变量不会“自动记住上一次值”。
为什么 Hooks 总和重渲染绑在一起
因为每次渲染都会形成一套新的变量绑定和新的闭包。
function Demo() {
const [count, setCount] = useState(0)
function handleClick() {
console.log(count)
}
return <button onClick={handleClick}>{count}</button>
}
这里的 handleClick 拿到的是“这次渲染里的 count”,不是某个永恒不变的变量。
所以一旦你把回调交给定时器、订阅、异步流程,就很容易遇到闭包旧值问题。
一个很重要的事实
重渲染不等于性能问题。
真正可能昂贵的是:
- 大量子树无意义重复计算
- 大列表渲染
- 昂贵计算重复执行
- effect 和副作用反复运行
为什么很多人会误解它
因为“组件函数重新执行”听起来像很重,但 React 的模型本来就是声明式重算,问题通常出在状态设计和更新边界,而不是重渲染这个词本身。
和优化 Hook 的关系
useMemo主要优化计算结果复用useCallback主要优化函数引用稳定useRef用于保存不该触发重渲染的值
常见误解
- 以为函数组件只会创建一次
- 以为“函数重新执行”就等于 DOM 整体重建
- 以为只要 rerender 就一定要上
memo - 以为所有性能问题都来自 rerender 次数
真正更值得先查的是:
- 状态是不是放错位置了
- 是否建了不该有的派生状态
- 是否写了不必要的 Effect
- 是否有昂贵计算或长列表
最短记忆方式
先接受“重渲染是正常现象”,再去定位真正昂贵的是哪一段工作。
面试要点
来自 react-rerender-interview-question 的面试视角整理。
一句话回答
React 组件常见会因为自己的 state 变化、父组件传入新 props,或 context 值变化而重渲染;但是否需要优化,要看这次重渲染有没有带来真正昂贵且无意义的工作。
最稳的回答主线
- state 变化会触发组件重新执行
- 父组件重渲染可能带动子组件一起重新执行
- context 更新也会影响消费者
- 真正要优化的是重复计算、大列表、昂贵副作用和错误状态边界
一个更完整的面试表达
可以这样答:
React 组件常见会因为自己的 state 更新、父组件传入新的 props、或者 context 值变化而重渲染。
但重渲染本身不等于性能问题,因为 React 的正常工作方式就是重新执行组件函数、计算新的 UI 描述,再决定真实 DOM 要不要更新。真正值得优化的,是这次 rerender 是否带来了昂贵且没有价值的工作。
面试里最好补的一句
我一般不会一上来就说“减少 rerender”,而会先看慢点到底是来自昂贵计算、长列表、错误状态设计,还是无意义的副作用重复执行。
一个常见误区
很多人会把“函数组件重新执行”误解成“整个 DOM 都重建了”。这不对。
更准确的过程是:
- 组件函数重新执行
- 生成新的 JSX 描述
- React 比较差异
- 只提交必要的真实更新
一个更工程化的优化顺序
- 先定位为什么会 rerender
- 再看这次 rerender 到底贵不贵
- 最后才考虑
memo、useMemo、useCallback
最短记忆方式
先问为什么会 rerender,再问这次 rerender 到底贵不贵。