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

React-Redux-Reselect应用优化:Day组件不必要重渲染排查

解决Reselect未减少Day组件重渲染的问题

听起来你遇到的核心问题是:明明用了Reselect,但更新单个计划时所有Day组件都重渲染,完全没达到预期的性能优化效果。我来帮你拆解下可能的原因,以及对应的解决方案:

一、核心问题分析

1. 选择器返回的数据粒度太大,未结合Day组件的日期过滤

你的makeGetRelevantObjectPlans选择器只筛选了和当前用户/项目/类型相关的所有计划,但没有过滤当前Day组件对应的日期。这意味着每个Day组件拿到的是该用户的全部计划,而不是当天的计划。

只要该用户的任何一个计划变化(哪怕是其他日期的),Day组件的planifications props就会变成新数组(因为lodash的filter每次都会返回新数组),React浅比较会认为props变化,触发重渲染。

2. Reselect缓存被无效触发

你的选择器依赖了getAllPlans(整个planifications对象),而每次更新单个计划时,reducer都会返回一个新的planifications对象(不可变更新的正确操作,但导致引用变化)。这会让所有依赖getAllPlans的选择器实例都重新计算,哪怕当前Day组件的计划根本没变化——因为Reselect的缓存是基于输入依赖的引用是否变化,而不是内容。

3. 选择器结果的引用不稳定

即使筛选后的计划内容和之前完全一致,_.filter也会返回一个新数组。React的浅比较会认为planifications props变了,从而触发重渲染,哪怕组件根本不需要更新。

二、针对性解决方案

方案1:让选择器筛选出当前Day的计划(最优先做)

首先修改选择器,加入Day组件的日期props,只返回当天的相关计划,从根源减少不必要的数据传递:

// 新增选择器:获取Day组件的日期props
export const getDayPropsDate = (state, props) => props.date;

// 修改makeGetRelevantObjectPlans,加入日期过滤
export const makeGetRelevantObjectPlans = () => {
  return createSelector(
    [getAllPlans, getGroup, getUserPropsId, getDayPropsDate],
    (planifications, group, user, date) => {
      switch (group.code) {
        case 'project':
          return _.filter(planifications, { projectId: user, planDate: date });
        case 'user':
          return _.filter(planifications, { userId: user, planDate: date });
        case 'type':
          return _.filter(planifications, { typeId: user, planDate: date });
        default:
          return [];
      }
    }
  );
};

这样,只有当当天的计划发生变化时,Day组件的planifications才会更新,其他日期的Day组件不会因为非当天计划的变化而触发重渲染。

方案2:用深比较缓存选择器结果,避免无意义的新数组

为了让选择器在内容不变时返回同一个数组引用,你可以用Reselect的自定义选择器创建器,结合深比较来缓存结果:

import { createSelectorCreator, defaultMemoize } from 'reselect';
import isEqual from 'lodash/isEqual';

// 创建支持深比较的选择器创建器
const createDeepEqualSelector = createSelectorCreator(
  defaultMemoize,
  isEqual
);

// 修改选择器使用createDeepEqualSelector
export const makeGetRelevantObjectPlans = () => {
  return createDeepEqualSelector(
    [getAllPlans, getGroup, getUserPropsId, getDayPropsDate],
    (planifications, group, user, date) => {
      switch (group.code) {
        case 'project':
          return _.filter(planifications, { projectId: user, planDate: date });
        case 'user':
          return _.filter(planifications, { userId: user, planDate: date });
        case 'type':
          return _.filter(planifications, { typeId: user, planDate: date });
        default:
          return [];
      }
    }
  );
};

这个方案会在选择器重新计算时,对比新结果和旧结果的内容:如果内容完全一致,就返回旧数组的引用,React浅比较会认为props没变化,不会触发重渲染。

方案3:优化Redux Store结构(长期性能优化)

如果你的数据量持续增长,可以考虑按日期分组存储计划,比如:

// 调整后的store结构
planifie: {
  // ...其他数据
  planificationsByDate: {
    "2019-05-16": [plan549, planXXX],
    "2019-05-17": [plan550, planYYY]
  }
}

然后修改选择器直接获取对应日期的计划列表,这样更新计划时只需要修改对应日期的数组,其他日期的数组引用不变,依赖这些数组的选择器不会重新计算,从根源避免无效重渲染。对应的reducer也需要调整,比如更新计划日期时,从旧日期数组移除,添加到新日期数组。

三、额外验证步骤

  1. 用React.memo包裹Day组件,确保组件只会在props真正变化时重渲染:
const Day = React.memo((props) => {
  // 组件逻辑
});
  1. 在componentDidUpdate中打印prevProps.planifications === props.planifications,验证引用是否稳定——如果返回true,说明缓存生效了。

内容的提问来源于stack exchange,提问作者Alicia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:48:42