RTK 状态修改后组件重渲染规则
一、 核心前提:RTK 不可变更新铁律
RTK 底层基于 Redux,状态变更的唯一有效方式是返回新的引用对象,这是组件能感知到状态变化的基础。
- Redux 浅比较机制:Redux 只对比 state 的引用地址,不对比对象内部属性。直接修改原 state(如
state.name = '新值'),引用地址不变 → Redux 判定状态未变 → 组件不渲染。 - RTK 的 Immer 自动优化:
createSlice/createReducer内置 Immer 库,允许在 reducer 中「看似直接修改 state」(如state.user.name = '张三'),Immer 会自动转换为不可变更新,生成新的 state 引用。
二、 组件重渲染的核心判定规则
当 RTK 中任意 state 被修改(生成新引用)后,所有使用 useSelector 的组件都会触发一次对比校验(发布订阅模式),最终是否渲染完全取决于对比结果。
1. 全局触发 → 局部判定的完整流程
| 步骤 | 具体操作 |
|---|---|
| 1 全局状态标记 | dispatch 触发 action → reducer 返回新 state 引用 → Redux 仓库标记「状态已变更」 |
| 2 全量执行回调 | 所有使用 useSelector 的组件,重新执行自身的 useSelector 回调函数,获取最新返回值 |
| 3 新旧值对比 | 拿最新返回值与上一次渲染的旧返回值做对比 |
| 4 渲染决策 | 对比结果不同 → 组件重渲染;对比结果相同 → 组件不渲染 |
2. useSelector 对比规则(核心中的核心)
对比规则完全由 useSelector 返回值的类型决定,无任何例外:
| 返回值类型 | 对比方式 | 渲染判定示例 |
|---|---|---|
| 基础类型(string/number/boolean/null/undefined) | 直接对比值是否相等 | name 从 李四→张三 → 值变 → 渲染;name 从 张三→张三 → 值不变 → 不渲染 |
| 引用类型(对象/数组/函数) | 只对比内存引用地址是否相等 | 新生成的 user 对象 → 地址变 → 渲染;返回临时新对象 {name, age} → 每次地址不同 → 无意义渲染 |
三、 高频误区与解决方案
1. 误区:返回临时引用类型导致无意义渲染
jsx
// ❌ 错误写法:每次执行都会返回新对象,地址永远不同
const userInfo = useSelector(state => ({
name: state.user.name,
age: state.user.age
}))
2. 解决方案(按优先级排序)
| 方案 | 写法示例 | 优势 |
|---|---|---|
拆分 useSelector(最优) | const name = useSelector(state => state.user.name)const age = useSelector(state => state.user.age) | 无性能损耗,逻辑清晰,仅值变时渲染 |
记忆化选择器 createSelector(复杂场景推荐) | const selectUser = state => state.userconst selectUserInfo = createSelector([selectUser], user => ({name: user.name, age: user.age})) | 依赖状态不变时,返回缓存的旧引用,避免重复计算 |
浅比较函数 shallowEqual(兜底方案) | import { shallowEqual } from 'react-redux'const userInfo = useSelector(state => ({name, age}), shallowEqual) | 对比对象第一层属性值,而非地址 |
四、 关键补充说明
- 深层属性修改的影响:修改深层属性(如
state.user.info.address)时,Immer 会生成新的address→新的info→新的user,最终user引用地址改变,订阅user的组件会渲染。 - 全量对比 ≠ 全量渲染:所有组件的
useSelector都会执行对比,但对比是极轻量的 JS 逻辑判断,无 DOM 操作;只有对比结果不同的组件才会触发重渲染,性能开销可忽略。 - 其他渲染触发因素:组件自身
useState状态变化、父组件重渲染(未用React.memo),也会导致组件渲染,与 RTK 状态无关。
五、 终极铁律(3 条即可覆盖所有场景)
- RTK 修改 state 后,所有用了
useSelector的组件都会触发一次对比校验。 - 组件是否渲染,唯一依据是
useSelector新旧返回值是否不同:基础类型比值,引用类型比地址。 - RTK 保证状态有效变更的核心是 Immer 自动实现不可变更新,生成新的 state 引用。