You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 19:22:43