useSelector中array.map()引发组件重渲染的原因及相关疑问解析
Redux useSelector 常见疑问解答
两个示例的区别
这两个写法本质上都存在「每次调用都返回新数组引用」的问题,但适用场景的侧重点不同:
- 第一个「不良示例」的核心问题是:组件可能仅依赖
todos的描述文本,但任何action(哪怕和todos完全无关)都会触发组件无意义重渲染。 - 第二个示例是在「按ID渲染列表项」的场景下,教程优先聚焦功能实现,暂时未提及性能优化——毕竟这个组件本身就是用来展示todos列表的,当todos变化时必须重渲染,但如果存在其他和todos无关的action,它依然会触发无意义重渲染,优化方案通常会在后续章节补充。
正确写法
针对这类返回新引用的选择器,官方推荐两种优化方式:
1. 使用记忆化选择器(Reselect)
通过@reduxjs/toolkit提供的createSelector创建记忆化选择器,它会缓存计算结果,只有当依赖的state切片真正变化时,才会重新执行计算并返回新引用:
import { createSelector } from '@reduxjs/toolkit' // 基础选择器,获取todos切片 const selectTodos = state => state.todos // 记忆化选择器,仅在selectTodos的结果变化时重新计算 const selectTodoIds = createSelector( [selectTodos], (todos) => todos.map(todo => todo.id) ) // 组件中使用 const todoIds = useSelector(selectTodoIds)
2. 使用浅比较替代严格相等
给useSelector传入第二个参数shallowEqual,它会比较数组/对象的内容是否一致,而非仅比较引用:
import { shallowEqual, useSelector } from 'react-redux' const selectTodoIds = state => state.todos.map(todo => todo.id) const todoIds = useSelector(selectTodoIds, shallowEqual)
能不能在useSelector中使用数组?
当然可以。只是要避免「每次调用都返回内容相同但引用不同的数组」。只要通过记忆化选择器保证引用的稳定性,或者用浅比较忽略引用差异,就可以安全在选择器中返回数组。
无意义重渲染的具体场景示例
假设你的Redux Store包含todos和user两个独立切片:
// Store初始状态 const initialState = { todos: [{ id: 1, text: '学习Redux' }], user: { name: '张三' } }
现在有一个修改用户名的action:
// 修改用户名的action creator const updateUserName = (newName) => ({ type: 'user/updateName', payload: newName })
当你dispatch这个action时,state.user会更新,但state.todos完全没有变化。但如果你的组件使用了state.todos.map(todo => todo.id)这种选择器,每次dispatch这个action,选择器都会返回一个新的数组引用。useSelector用===比较前后引用,发现不一致就会触发组件重渲染——但组件依赖的todos数据根本没变化,这就是典型的无意义重渲染。
内容的提问来源于stack exchange,提问作者MKF
相关产品推荐
相关产品推荐

