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

ReactJS Reselect容器重复渲染问题求助

React Reselect: 解决Connect组件重复渲染问题(Perf.printWasted显示多次渲染)

嘿,我之前也踩过这个坑,给你详细说说怎么解决!你遇到的Connect(Single Entries Index)组件明明只有1个实例却重复渲染的问题,核心原因出在Reselect默认的缓存比较逻辑上。

先看看你的原代码:

import React, { Component } from 'react'; 
import { connect } from 'react-redux'; 
import { createSelector } from 'reselect'; 
import { shape } from 'prop-types'; 
import Entry from '/components/entries/Entry'; 

class Entries extends Component { 
  static displayName = 'Single Entries Index'; 
  static propTypes = { 
    entries: shape() 
  }; 

  shouldComponentUpdate(nextProps) { 
    if (this.props.entries.size !== nextProps.entries.size) { 
      return true; 
    } 
    return false; 
  } 

  render() { 
    const { entries } = this.props; 
    return ( 
      <ul className="entries"> 
        {entries.map(entry => ( 
          <Entry key={entry.get('id')} id={entry.get('id')} /> 
        ))} 
      </ul> 
    ); 
  } 
}; 

const makeGetEntries = createSelector( 
  state => state.data, 
  data => data.get('entries') 
); 

const mapStateToProps = (state) => { 
  return { entries: makeGetEntries(state) }; 
}; 

export default connect(mapStateToProps)(Entries);

问题分析

Reselect默认的createSelector使用**浅比较(===)**来判断是否需要更新缓存。你这里返回的data.get('entries')看起来是Immutable.js的集合(从get、size方法能看出来),当Redux store有其他无关更新时,哪怕entries本身的内容没变化,state.data的引用可能发生了改变,导致默认selector认为依赖项更新了,返回一个新的entries引用。

而你的shouldComponentUpdate只判断了entries.size,但如果entries内容变化但size不变会漏判,不过当前更紧急的是:每次selector返回新引用时,哪怕内容完全一样,connect都会触发组件重新渲染——这就是Perf工具显示"重复渲染"的原因。

解决方案:自定义深比较Selector

@Logar给出的方案非常靠谱,通过createSelectorCreator替换默认的比较逻辑,改用深比较来判断缓存是否有效。这样只要entries的内容没变化,selector就会返回缓存的旧引用,不会触发不必要的渲染。

修改后的代码:

import { createSelectorCreator, defaultMemoize } from 'reselect';
import isEqual from 'lodash/isEqual'; // 或者你用的其他深比较工具

const createDeepEqualSelector = createSelectorCreator(
  defaultMemoize,
  isEqual
); 

const makeGetEntries = createDeepEqualSelector(
  state => state.data,
  data => data.get('entries')
); 

const mapStateToProps = (state) => {
  return { entries: makeGetEntries(state) };
}; 

export default connect(mapStateToProps)(Entries);

额外优化:适配Immutable.js的比较

如果你用的是Immutable.js,更推荐用它自带的is方法来做比较,比lodash的isEqual更高效,也更贴合Immutable的数据结构:

import { createSelectorCreator, defaultMemoize } from 'reselect';
import { is } from 'immutable';

const createImmutableSelector = createSelectorCreator(
  defaultMemoize,
  is
);

const makeGetEntries = createImmutableSelector(
  state => state.data,
  data => data.get('entries')
);

这样调整后,再用Perf.printWasted检查,应该就不会看到那个不必要的重复渲染了!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:56:29