使用 shouldComponentUpdate 每秒重新渲染块组件

Using shouldComponentUpdate for block component re-render every second

那个游戏是用 react 开发的,redux.I 不是 react-redux 开发人员(我是 .net 开发人员),但我必须继续那个项目,所以我是 react 和 redux 的新手 那个游戏性能太差 android phones.So 我分析 project.I 看到组件渲染方法工作每个 second.My 组件每秒包含超过 30 个其他 components.So它会重新渲染,这会导致某些旧 android 手机

性能不佳

为什么 React 组件每秒都重新渲染?我可以阻止它吗? 我搜索那个问题,我看到解决方案是 shouldComponentUpdate function

shouldComponentUpdate(nextProps,nextState) {

        console.log(nextProps.gameStore.get('state'));//waiting
        console.log(this.props.gameStore.get('state'));//waiting

        console.log(this.state);
        console.log(nextState);
        if (nextProps.gameStore.get('state')==this.props.gameStore.get('state')) {
            return false;
        }
        else {
            return true;
        }
        }

但是在这个函数中 nextstate 和这个 state 是一样的, nextProps.gameStore.get('state') 和 this.props.gameStore.get('state') 是 same.Why 下一个状态和当前状态相同?我该怎么办?我使用构造函数但是它还是一样这里是我所有的组件代码

    import React from 'react';
import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
//import { default as HTML5Backend } from 'react-dnd-touch-backend';

import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { bindFirstArguments } from 'utils/bindFirstArgument';
import * as OkeyGameActions from 'actions/OkeyGameActions';
import * as OkeyNetActions from 'actions/OkeyNetActions';

import * as OkeyChatActions from 'actions/Chatactions';

import { SeatDirection } from 'constants/AppConstants';

import { OkeyScoreboardDialog }
from 'components/OkeyScoreboardDialog/OkeyScoreboardDialog';
import OkeyMatchResultDialog
from 'components/OkeyMatchResultDialog/OkeyMatchResultDialog';

import OkeyRackWrapper from 'components/OkeyRackWrapper/OkeyRackWrapper';
import OkeyTopToolbar from 'components/OkeyTopToolbar/OkeyTopToolbar';
import OkeyTableToolbar from 'components/OkeyTableToolbar/OkeyTableToolbar';
import OkeyTableCenter from 'components/OkeyTableCenter/OkeyTableCenter';

import CustomDragLayer from 'components/CustomDragLayer/CustomDragLayer';
import MessageList from 'components/chat/MessageList';
import PrivateEastMessageList from 'components/chat/PrivateEastMessageList';
import PrivateNorthMessageList from 'components/chat/PrivateNorthMessageList';
import PrivateWestMessageList from 'components/chat/PrivateWestMessageList';
import PrivateSouthMessageList from 'components/chat/PrivateSouthMessageList';

import './_OkeyGame.scss';

function toJS(item) {
  if (item === null) {
    return null;
  }
  //var item1=item.toJS();
  //if (item1.color==='BLACK') {
  //    var a='a';
    //}

  if (item == undefined) {
      return;
  }

  return item.toJS();
}

function getRelativeDirection(selfSeat, direction) {
  let relativeDirection = direction;

  if (selfSeat >= 0) {
    relativeDirection = (selfSeat - direction + 4) % 4;
  }
  return relativeDirection;
}

class OkeyGame extends React.Component {

    constructor(props) {
  super(props);
    }





    shouldComponentUpdate(nextProps,nextState) {

        console.log(nextProps.gameStore.get('state'));//waiting
        console.log(this.props.gameStore.get('state'));//waiting

        console.log(this.state);
        console.log(nextState);
        if (nextProps.gameStore.get('state')==this.props.gameStore.get('state')) {
            return false;
        }
        else {
            return true;
        }
        }
    render() {

    const { dispatch, gameStore, gamePlay, playRules } = this.props;

    let actions = bindActionCreators(OkeyGameActions, dispatch);

    let netActions = bindActionCreators(OkeyNetActions, dispatch);
    const currentTurn = gameStore.get('currentTurn');

    const playState = {
      selectedStone: gamePlay.get('selectedStone'),
      gosterge: gamePlay.get('gosterge'),
      middleStoneCount: gamePlay.get('middleStoneCount'),
      currentTurn: currentTurn
    };


    if (playState.gosterge != undefined) {
        window.localStorage.setItem('gostergeNumber', playState.gosterge._root.entries[0][1]);
        window.localStorage.setItem('gostergeColor', playState.gosterge._root.entries[1][1]);
    }


    const hasOpenedStonesThisTurn = {
      hasOpenedSequenceThisTurn: playRules.get('hasOpenedSequenceThisTurn'),
      hasOpenedPairsThisTurn: playRules.get('hasOpenedPairsThisTurn')
    };

    const rules = {
      canOpenSequence: playRules.get('canOpenSequence'),
      canOpenPairs: playRules.get('canOpenPairs'),
      canWithdraw: playRules.get('canWithdraw'),
      canDiscard: playRules.get('canDiscard'),
      canCollectOpen: playRules.get('canCollectOpen'),
      canLeaveTaken: playRules.get('canLeaveTaken'),
      canProcessStone: playRules.get('canProcessStone')
    };

    const discardMiniBoxes = {
      discardMiniBoxPairs: gamePlay.get('pairs'),
      discardMiniBoxSequence: gamePlay.get('sequence')
    };

    const selfSeat = gameStore.get('selfSeat');

    const { westSeat, eastSeat, northSeat, southSeat } =
    {
      westSeat: getRelativeDirection(selfSeat, SeatDirection.WEST),
      eastSeat: getRelativeDirection(selfSeat, SeatDirection.EAST),
      northSeat: getRelativeDirection(selfSeat, SeatDirection.NORTH),
      southSeat: getRelativeDirection(selfSeat, SeatDirection.SOUTH)
};

    const players = {
      selfSeat: selfSeat,
      pSouth: {
        seatId: southSeat,
        discardStones: gamePlay.getIn(['discardStones', southSeat]),
        profile: toJS(gameStore.getIn(['players', southSeat])),
        dispatch: dispatch
      },
      pNorth: {
        seatId: northSeat,
        discardStones: gamePlay.getIn(['discardStones', northSeat]),
        profile: toJS(gameStore.getIn(['players', northSeat])),
        dispatch: dispatch
      },
      pEast: {
        seatId: eastSeat,
        discardStones: gamePlay.getIn(['discardStones', eastSeat]),
        profile: toJS(gameStore.getIn(['players', eastSeat])),
        dispatch: dispatch
      },
      pWest: {
        seatId: westSeat,
        discardStones: gamePlay.getIn(['discardStones', westSeat]),
        profile: toJS(gameStore.getIn(['players', westSeat])),
        dispatch: dispatch
      }
    };


    let profiles = [
              players.pSouth.profile,
              players.pEast.profile,
              players.pNorth.profile,
              players.pWest.profile
    ];

    localStorage.setItem("selfSeat", selfSeat);
    localStorage.setItem("roomID", gameStore.get('id'));

    if (selfSeat == 0) 
        profiles = [players.pSouth.profile,players.pEast.profile,players.pNorth.profile,players.pWest.profile];

    else if (selfSeat == 1) 
        profiles = [players.pWest.profile,players.pSouth.profile,players.pEast.profile,players.pNorth.profile];

    else if (selfSeat == 2) 
        profiles = [players.pNorth.profile,players.pWest.profile,players.pSouth.profile,players.pEast.profile];

    else if (selfSeat == 3) 
        profiles = [players.pEast.profile,players.pNorth.profile,players.pWest.profile,players.pSouth.profile];


    const matchState = {
      name: gameStore.getIn(['options', 'name']),
      maxRounds: gameStore.getIn(['options', 'rounds']),
      stake: gameStore.getIn(['options', 'stakes']),
      round: gameStore.get('round')
    };



    const owner = gamePlay.get('ownerID');

    const scoreboard = gameStore.get('scoreboard');

    const matchResult = gameStore.get('matchResult');

    const restCountdown = gameStore.get('restCountdown');

    const roomState = gameStore.get('roomState');



const { messageList } = this.props;
const { privateEastMessageList } = this.props;
const { privateNorthMessageList } = this.props;
const { privateWestMessageList } = this.props;
const { privateSouthMessageList } = this.props;

let chatActions = bindActionCreators(OkeyChatActions, dispatch);
// const  dispatch1  = this.props

    // each action has a first argument of room id
netActions = bindFirstArguments(netActions, gameStore.get('id'));

    let from = gameStore.get('from');
    let to = gameStore.get('to');
    let gift = gameStore.get('gift');

    let from1 = gameStore.get('from1');
    let to1 = gameStore.get('to1');
    let gift1 = gameStore.get('gift1');

    let from2 = gameStore.get('from2');
    let to2 = gameStore.get('to2');
    let gift2 = gameStore.get('gift2');

    let from3 = gameStore.get('from3');
    let to3 = gameStore.get('to3');
    let gift3 = gameStore.get('gift3');

    let arayan = gameStore.get('arayan');
    let aranan = gameStore.get('aranan');

    return (
        <div className="game-background" style={{background: 'url(http://okey101.xyz/staticImg/background.png)',backgroundSize:'cover'}}>
      <div className="okey-game flex-centered-column">
       <CustomDragLayer isMini={gamePlay.get('isOver') > 0}></CustomDragLayer>


        <MessageList {...chatActions} {...netActions} messageList={messageList} />

             <OkeyScoreboardDialog profiles={profiles}
      scoreboard={scoreboard} />


        <OkeyMatchResultDialog matchResult={matchResult}
        {...netActions}
        {...actions}
        roomState={roomState}/>


        <OkeyTopToolbar {...netActions}
                        {...matchState}
                        profiles={profiles}/>

        <OkeyTableCenter {...actions}
                         {...netActions}
                         {...playState}
                         {...rules}
                         {...discardMiniBoxes}
                         {...players}
                         owner={owner}
                         messageList={messageList}
                         privateEastMessageList={privateEastMessageList}
                         privateNorthMessageList={privateNorthMessageList}
                         privateWestMessageList={privateWestMessageList}
                         privateSouthMessageList={privateSouthMessageList}
                         from={from} 
                         to={to}
                         gift={gift}
                         from1={from1} 
                         to1={to1}
                         gift1={gift1}
                         from2={from2} 
                         to2={to2}
                         gift2={gift2}
                         from3={from3} 
                         to3={to3}
                         gift3={gift3}
                         arayan={arayan}
                         aranan={aranan}
                         stones={gamePlay.get('stones')}/>

        <OkeyRackWrapper {...actions}
                         {...netActions}
                         {...playState}
                         stones={gamePlay.get('stones')}
                         stoneGroups={gamePlay.get('stoneGroups')}/>
        <OkeyTableToolbar {...actions}
                          {...netActions}
                          {...rules}
                          restCountdown={restCountdown}
                          currentTurn={currentTurn}
                          {...hasOpenedStonesThisTurn}
                          roomState={roomState}
                          stones={gamePlay.get('stones')}
                          {...discardMiniBoxes}
                          okeyStone={gamePlay.get('okeyStone')}/>

      </div>

</div>
    );
  }
}

const mapStateToProps = (state => ({
  gameStore: state.gameStore,
  gamePlay: state.gamePlay,
  playRules: state.playRules,
  messageList: state.MessageList,
  privateEastMessageList: state.PrivateEastMessageList,
  privateNorthMessageList: state.PrivateNorthMessageList,
  privateWestMessageList: state.PrivateWestMessageList,
  privateSouthMessageList: state.PrivateSouthMessageList
}));

const OkeyGameWithDnD = DragDropContext(HTML5Backend)(OkeyGame);

export default connect(mapStateToProps)(OkeyGameWithDnD);

编辑:使用 Aftab Khan 指令,我将组件更改为 PureComponent,但页面打不开,控制台中也没有错误

我改成

const mapStateToProps = (state => ({
    gameStore: toJS(state.gameStore),
    gamePlay: toJS(state.gamePlay),
    playRules: toJS(state.playRules),
    messageList: toJS(state.MessageList),
    privateEastMessageList: toJS(state.PrivateEastMessageList),
    privateNorthMessageList: toJS(state.PrivateNorthMessageList),
    privateWestMessageList: toJS(state.PrivateWestMessageList),
    privateSouthMessageList: toJS(state.PrivateSouthMessageList)
}));

但还是不行 然后我把它改成这个

const mapStateToProps = (state => ({
    gameStore: state.gameStore.toJS(),
    gamePlay: state.gamePlay.toJS(),
    playRules: state.playRules.toJS(),
    messageList: state.MessageList.toJS(),
    privateEastMessageList: state.PrivateEastMessageList.toJS(),
    privateNorthMessageList: state.PrivateNorthMessageList.toJS(),
    privateWestMessageList: state.PrivateWestMessageList.toJS(),
    privateSouthMessageList: state.PrivateSouthMessageList.toJS()
}));

但它仍然无法在浏览器中打开

看来你需要 React.PureComponent。 这是它的link。 Link

基本上它所做的是在渲染每个组件之前,它会检查组件的道具或状态是否有任何变化。如果没有变化,它不会重新渲染。

看起来您在 reducer 中使用了不可变数据结构。在你的 mapStateToProps 中,将所有不可变数据更改为调用 toJS() 方法的普通 JS 对象。然后 PureComponent 将按预期工作。

关于您的组件,我注意到的很少

  1. render 方法对子组件进行大量初始化

  2. shouldComponentUpdate 是不必要的,因为它只处理道具(这是预期的,因为你所有的状态都在 redux 商店中)。

  3. PureComponent 是不必要的,因为您正在使用 connect() 将它连接到商店。您的组件没有状态,它更好地发挥作用 components created using a simple es6 arrow function

对于 2. 和 3. 请参考 note 优化 connect() 为您创建容器。

现在,如果不检查组件的每一行,就不可能告诉您为什么组件每秒都在渲染。但是,仔细考虑以下内容可以帮助您确保仅在必要时节俭地触发重新渲染。

1.保持 render() 方法尽可能轻量级 render 可能是所有组件中调用次数最多的方法。更臃肿的是你渲染周期会很慢。你可以把你 localStorage.setItems 全部移到 componentWillReceiveProps。这些 synchronous LocalStorage calls 中的每一个都占用了渲染时间。

2。删除 shouldComponentUpdate。相反,只将那些道具传递给它绝对需要的组件。制作尽可能多的组件无状态纯函数

shouldComponentUpdate 是组件的逃生舱口,组件需要渲染更多道具。 sCU 的缺点:它在每个组件重新渲染之前运行。在某些情况下,它可以减慢速度而不是加快速度。请参阅 jimfb 的评论 here。这支持我将尽可能多的组件转换为无状态功能组件的建议 - 可能包括您在此处发布的组件

3。如果您的组件是无状态的,则不需要 PureComponent javascript 函数只接受它们需要的道具。