如何基于.filter()的输出避免Redux中的重复渲染?
嘿,我来帮你把这个问题掰扯清楚~首先得夸你一句,你已经意识到直接在useSelector里用filter会每次返回新数组,导致组件平白无故重渲染的问题,这已经踩中了Redux性能优化的关键点!
先说说你第二个方案的问题:你把filter移到useSelector外面,写成const activeEntities = useSelector(state => state.entities).filter(...),其实这个做法并没有完全解决问题——虽然useSelector只会在state.entities变化时触发,但只要组件因为任何原因重渲染(比如其他props变化、组件自身state更新),这个filter都会重新执行一遍生成新数组;而且更关键的是,哪怕只是state.entities里的某个非活跃实体变了,useSelector也会返回新的entities数组,然后filter生成新的activeEntities,组件还是会被触发重渲染,这显然不是你想要的“只在活跃实体变化时重渲染”的效果。
接下来聊聊你提到的createSelector——你觉得它和第二个方案功能等价?这可真的错啦!createSelector是Redux Toolkit(或者独立的reselect库)提供的记忆化选择器,它会缓存计算结果,只有当它依赖的输入(也就是这里的state.entities.items)发生实际的、会影响最终结果的变化时,才会重新执行后面的filter逻辑。而且当你把它用到useSelector里时,useSelector会对比记忆化后的结果引用,只有当活跃实体列表真的变了,才会触发组件重渲染。
那正确的姿势应该是怎样的?
首先,如果你用的是Redux Toolkit,createSelector已经内置了,不用额外安装;如果是传统Redux,需要先装reselect包。然后定义你的记忆化选择器:
// 如果你用Redux Toolkit import { createSelector } from '@reduxjs/toolkit'; // 如果你用传统Redux // import { createSelector } from 'reselect'; // 先定义一个基础选择器,用来获取原始的entities列表 const selectAllEntities = state => state.entities.items; // 再创建记忆化的活跃实体选择器 const selectActiveEntities = createSelector( [selectAllEntities], // 依赖的输入选择器数组 (entities) => entities.filter(entity => entity.active) );
然后在组件里这样使用:
const activeEntities = useSelector(selectActiveEntities);
这个方案的优势在哪?
- 精准触发重渲染:只有当
state.entities.items变化,并且过滤后的活跃实体列表和之前完全不同时,selectActiveEntities才会返回新的数组引用,useSelector才会触发组件重渲染——完美符合你“只在活跃实体变化时重渲染”的需求。 - 避免重复计算:就算组件因为其他原因重渲染,
selectActiveEntities会直接返回缓存的结果,不会重新执行filter,节省了不必要的计算开销。
再回头对比你的第二个方案:它只是减少了useSelector因返回新引用导致的重渲染,但没法做到“只关注活跃实体变化”,而且每次组件渲染都要重新跑一遍filter,性能上不如记忆化选择器。
最后补个小提醒:如果你的state.entities是一个对象(比如用normalized数据结构),还可以调整输入选择器来更精细地控制记忆化,但对于你当前的场景,上面的方案已经完全够用啦。
备注:内容来源于stack exchange,提问作者kiwikodes

