当数组未更改时,具有数组属性的React/Redux/Pure Component重新渲染 [英] React / Redux / Pure Component with an array property re-renders when the array has not changed

查看:127
本文介绍了当数组未更改时,具有数组属性的React/Redux/Pure Component重新渲染的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a React / Redux app that renders a grid of PureComponents. I want the components to only re-render when a prop value has changed, but in fact all components re-render on any update to store. The issue appears to be caused by an array property.

我已经在此沙箱中重现了该问题.在此最小示例中,呈现了Cell组件的两个实例.每个按钮都显示一个单独存储在商店中的值,并且可以使用按钮分别进行递增或递减操作.

I've reproduced the issue in this sandbox. In this minimal example, two instances of the Cell component are rendered. Each displays a value that is separately recorded in the store, and can be incremented or decremented separately with a button.

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);

如果您检查控制台,您应该看到单击一个按钮时,两个单元都重新呈现.

If you check the console, you should see that when you click one button, both Cells re-render.

如果您在mapStateToProps中注释掉myArray道具,则行为会发生变化,因此只有您单击的单元格才会重新渲染.

If you comment out the myArray prop in mapStateToProps, the behaviour changes so that only the cell you've clicked re-renders.

由于静态数组属性,因此看来对组件的任何更改都会重新渲染PureComponent.

So it appears that PureComponent is re-rendering on any change to the store, because of the static array property.

在我的真实应用中,数组也将来自商店,并且如果数组值发生更改,Cell应该重新渲染,但是沙箱示例显示了即使静态数组属性也将触发重新渲染.

In my real app the array would come from the store also, and Cell should re-render if an array value changes, but the sandbox example shows how even a static array property triggers re-render.

是否有任何方法可以为PureComponent提供数组道具,并仅在道具更改时才重新渲染它?谢谢.

Is there any way to provide an array prop to a PureComponent and have it only re-render when a prop changes? Thank you.

更新了沙箱按照Domino987的建议将myArray移到存储中,并添加一个函数来计算Cell组件所需的子数组-这就是我的真实应用程序所做的.我通过重新选择和重新选择添加了记忆,并且使它成为功能组件而不是PureComponent.据我所知,这现在可以正常工作-单击一个按钮时,仅一个Cell会重新渲染.是的!

I've updated the sandbox to move myArray into the store as suggested by Domino987 and to add a function to calculate the sub-array required by the Cell component - this is what my real app does. I've added memoization with reselect and re-reselect, and I've made it a functional component instead of PureComponent. As far as I can see, this is now working - only one Cell re-renders when you click a button. Yay!

在actions.js中:

In 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中:

in 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)
}};

推荐答案

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

You are creating a new array on every mapStateToProps call with myArray: [0, 1, 2]. Because of this, mapStateToProps always returns an object with a new instance of an array.

Redux比以前的道具做了一个浅浅的比较. myArray的引用发生了变化,因为oyu在调用期间创建了一个新的引用,并且所有单元格都被覆盖.通过删除myArray: [0, 1, 2]行,它可以按预期方式工作.将该阵列移到redux中,这样就不会在每个调用中都产生新的调用.

Redux than does a shallow comparison of the previous props. The references of myArray changed, since oyu created a new one during the call and all cells get udpated. By removing the myArray: [0, 1, 2] line, it works as expected. Move that array into redux, so that you do not generate a new one of every call.

纯组件也是无用的,因为redux已经进行了prop比较,而纯组件又做了同样的事情,所以浪费了,因为它对于redux和pure组件总是具有相同的结果,并且工作更多.

The pure component is also useless, since redux already does a prop comparison and the pure component does the same thing again, so that is wasted since it always has the same result for redux and the pure component and is just more work.

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

Update: You are still creating a new array on every mapSateToPropsCall with the map function in getMyArray, since map returns a new array. Why do you not make the array static or memoize that, so that you only generate a new one, if needed?

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

Why do you not change how you save the data so that state.myArray[index] returns the needed array?

您仍然可以将PureCompoennt转换为Component.

And you still can transform the PureCompoennt to Component.

这篇关于当数组未更改时,具有数组属性的React/Redux/Pure Component重新渲染的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆