【问题标题】:React / Redux / Pure Component with an array property re-renders when the array has not changed具有数组属性的 React / Redux / Pure Component 在数组未更改时重新渲染
【发布时间】:2020-04-25 17:51:48
【问题描述】:

我有一个渲染 PureComponents 网格的 React / Redux 应用程序。我希望组件仅在道具值更改时重新渲染,但实际上所有组件都会在任何要存储的更新时重新渲染。该问题似乎是由数组属性引起的。

我已在this sandbox 中重现了该问题。在这个最小示例中,渲染了 Cell 组件的两个实例。每个都显示一个单独记录在存储中的值,并且可以通过一个按钮单独增加或减少。

Cell.js

import React, { PureComponent } from "react";
import { connect } from "react-redux";

import { incrementAction, decreaseAction } from "./actions";

class Cell extends PureComponent {
  render() {
    const { index, value, incrementAction, decreaseAction } = this.props;
    console.log("render cell with index", index);
    return (
      <div>
        <h1>{value}</h1>
        <button onClick={incrementAction}>increment</button>
        <button onClick={decreaseAction}>decrease</button>
      </div>
    );
  }
}

const mapStateToProps = (state, ownProps) => ({
  value: ownProps.index === 1 ? state.value1 : state.value2,
  myArray: [0, 1, 2]
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  incrementAction: () => dispatch(incrementAction(ownProps.index)),
  decreaseAction: () => dispatch(decreaseAction(ownProps.index))
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Cell);

如果您检查控制台,您应该会看到,当您单击一个按钮时,两个单元都会重新渲染。

如果您在 mapStateToProps 中注释掉 myArray 属性,则行为会发生变化,因此只有您单击的单元格会重新呈现。

因此,由于静态数组属性,PureComponent 似乎在对存储进行任何更改时重新渲染。

在我的真实应用中,数组也来自存储,如果数组值发生变化,Cell 应该重新渲染,但沙盒示例显示了静态数组属性如何触发重新渲染。

有什么方法可以为 PureComponent 提供一个数组 prop 并让它仅在 prop 更改时重新渲染?谢谢。

编辑:我 updated the sandbox 按照 Domino987 的建议将 myArray 移动到存储中,并添加一个函数来计算 Cell 组件所需的子数组 - 这是我真正的应用程序所做的。我已经添加了重新选择和重新选择的记忆,我已经把它变成了一个功能组件而不是 PureComponent。据我所知,这现在可以工作了——当你点击一个按钮时,只有一个 Cell 会重新渲染。耶!

在actions.js中:

import createCachedSelector from "re-reselect";

export function getMyArray(state, index) {
  console.log("getMyArrayCached for index", index);
  return state.myArray;
}

export function getIndex(state, index) {
  return index;
}

export const getMyArrayCached = createCachedSelector(
  getMyArray,
  getIndex,
  (myArray, index) =>
    myArray.map(elm => {
      return elm[index - 1];
    })
)((_state_, index) => index);

在 reducer.js 中:

const initialState = {
  value1: 0,
  value2: 0,
  myArray: [[1, 2], [1, 2], [1, 2]]
};

在 Cell.js 中:

const mapStateToProps = (state, ownProps) => {
  const value = ownProps.index === 1 ? state.value1 : state.value2;

  return {
  value,
  myArray: getMyArrayCached(state, ownProps.index)
}};

【问题讨论】:

    标签: reactjs optimization redux


    【解决方案1】:

    您正在使用myArray: [0, 1, 2] 在每个 mapStateToProps 调用上创建一个新数组。 因此,mapStateToProps 总是返回一个带有新数组实例的对象。

    Redux 比之前的 props 做了一个浅比较。 myArray 的引用发生了变化,因为 oyu 在调用期间创建了一个新的并且所有单元格都得到了更新。通过删除 myArray: [0, 1, 2] 行,它可以按预期工作。将该数组移动到 redux 中,这样您就不会在每次调用时生成一个新数组。

    纯组件也没有用,因为 redux 已经做了一个 prop 比较,而纯组件又做了同样的事情,所以这是浪费的,因为它对于 redux 和纯组件总是有相同的结果,而且工作量更大。

    更新: 您仍在使用 getMyArray 中的 map 函数在每个 mapSateToPropsCall 上创建一个新数组,因为 map 返回一个新数组。为什么不将数组设为静态或记忆它,以便只在需要时生成一个新数组?

    为什么不改变保存数据的方式,让state.myArray[index] 返回所需的数组?

    您仍然可以将 PureCompoent 转换为 Component。

    【讨论】:

    • 谢谢!我已根据您的建议更新了我的沙箱,并将其扩展为以我的真实应用程序的方式计算 myArray。计算导致重新渲染。我会用新的沙盒更新问题,如果您有任何建议,我将不胜感激。
    • 查看我的回答 ;)
    • 再次感谢。我添加了重新选择和重新选择的记忆,查看现在更新的沙箱,但我认为它不起作用,我看不出我做错了什么。缓存功能在每次更新时运行,并且单元仍然全部重新渲染。 myArray 来自数据库,更改其结构并非易事。
    • 啊!我想我现在可以工作了!我将更新沙盒并进行编辑以显示新版本。
    • 是的,你就是这样做的,你需要翻转选择器中的功能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-29
    • 2020-06-01
    相关资源
    最近更新 更多