chanjianyi
chanjianyi

Reputation: 615

How to improve the performance with big state in Reactjs?

This is what I wrote in react. it called game of life.

I make a cell as div, and store all the cell in one state object ,every cell has cell name look like "0_0", "0_1", "0_2" .... and has own alive status and neighbor. so the App state looks like that:

enter image description here

and I make a time loop for check every cell's status.

but as you see, it with very bad performance although there are not so much cells in this case.

I thought it will get good performance by the virtual dom.

So how to improve the performance in this case?

   
class Cell extends React.Component {
    constructor(props) {
        super(props);
    }
    shouldComponentUpdate(nextProps, nextState) {
        return nextProps.alive !== this.props.alive;
    }

    render() {
        let className = ['life'];
        if (this.props.alive) {
            className.push('alive');
        }

        return (
            <div className={className.join(' ')}></div>
        );
    }
}

let lifeSize = 5;
let w = 150;
let h = 150;
let chance = 0.75;
let stopTime = 333;
class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {};
        //this.cellsDiv=[];
        //console.log(store);
        this.checkAlive = this.checkAlive.bind(this);
        this.tick = this.tick.bind(this);
        this.runTime = 1;


        //per_width = per_width < 5 ? 10 : per_width;
        for (let i = 0; i < parseInt(h / lifeSize); i++) {
            for (let j = 0; j < parseInt(w / lifeSize); j++) {
                let neighborCells = [];
                neighborCells.push((i - 1) + '_' + (j - 1));
                neighborCells.push((i - 1) + '_' + (j + 1));
                neighborCells.push((i + 1) + '_' + (j - 1));
                neighborCells.push((i + 1) + '_' + (j + 1));
                neighborCells.push(i + '_' + (j - 1));
                neighborCells.push(i + '_' + (j + 1));
                neighborCells.push((i + 1) + '_' + j);
                neighborCells.push((i - 1) + '_' + j);
                this.state[i + '_' + j] = {};
                this.state[i + '_' + j]['alive'] = (Math.random() > chance);
                this.state[i + '_' + j]['neighbor'] = neighborCells;
            }
        }



    }
    checkAlive(cellName) {

        //console.log(neighborCells);
        let o = this.state[cellName];
        //console.log(i,j);
        let neighborCells = o['neighbor'];

        let alivecount = 0;
        for (let cell in neighborCells) {
            //console.log(neighborCells[cell],this.state[neighborCells[cell]]);
            if (this.state[neighborCells[cell]]) {
                if (this.state[neighborCells[cell]]['alive']) {
                    alivecount++;
                }
            }
        }
        //let alive = this.state[i + '_' + j]['alive'];
        //console.log(alive,alivecount);
        if (o['alive']) {
            if (alivecount < 2 || alivecount > 3) {
                o['alive'] = false;
            }
        } else {
            if (alivecount == 3) {
                o['alive'] = true;
            }
        }
        //console.log(o);
        let cells = {};
        cells[cellName] = {};
        cells[cellName]['alive'] = o['alive'];
        cells[cellName]['neighbor'] = o['neighbor'];
        this.setState(cells);
    }


    tick() {
        //console.log(this.runTime,stopTime);
        if (this.runTime >= stopTime) {
            clearInterval(this.timer);
        }
        
        //console.log(this.state);
        for (let cellName in this.state) {
            this.checkAlive(cellName);
        }

		this.runTime++;
        //console.log(this.state);
        //this.setState({alive:alive});
    }

    componentDidMount() {
        this.timer = setInterval(this.tick, 1000);
    }

    componentWillUnmount() {
        clearInterval(this.timer);
    }

    render() {
    
        return (
            <div id="show">
            { Object.keys(this.state).map((k, index) => <Cell key={k} alive={this.state[k]['alive']}/>) }
			
			</div>
        );

    }
}

ReactDOM.render(
    <App/>,
    document.getElementById('app')
);
#show {
  width: 150px;
  height: 150px;
  border: 1px solid black;
  xbackground: #f0f0f0;
  margin: 0;
  padding: 0;
}
.life {
  width: 5px;
  height: 5px;
  xborder: 1px solid black;
  background: white;
  float: left;
  xmargin: 1px;
}
.alive {
  background: black;
}
body {
  margin: 0;
  padding: 0;
  xbackground: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Upvotes: 0

Views: 420

Answers (2)

chanjianyi
chanjianyi

Reputation: 615

Accoding to Diogo's answer I got a better performance now

although increase the cells and with faster time loop:

class Cell extends React.Component {
    constructor(props) {
        super(props);
    }
    shouldComponentUpdate(nextProps, nextState) {
        return nextProps.alive !== this.props.alive;
    }

    render() {
        let className = ['life'];
        if (this.props.alive) {
            className.push('alive');
        }

        return (
            <div className={className.join(' ')}></div>
        );
    }
}

let cellSize = 5;
let w = 400;
let h = 400;
let chance = 0.85;
let stopTime = 200;
class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {};
        //this.cellsDiv=[];
        //console.log(store);
        this.checkAlive = this.checkAlive.bind(this);
        this.tick = this.tick.bind(this);
        this.runTime = 1;


        //per_width = per_width < 5 ? 10 : per_width;
        for (let i = 0; i < parseInt(h / cellSize); i++) {
            for (let j = 0; j < parseInt(w / cellSize); j++) {
                let neighborCells = [];
                neighborCells.push((i - 1) + '_' + (j - 1));
                neighborCells.push((i - 1) + '_' + (j + 1));
                neighborCells.push((i + 1) + '_' + (j - 1));
                neighborCells.push((i + 1) + '_' + (j + 1));
                neighborCells.push(i + '_' + (j - 1));
                neighborCells.push(i + '_' + (j + 1));
                neighborCells.push((i + 1) + '_' + j);
                neighborCells.push((i - 1) + '_' + j);
                this.state[i + '_' + j] = {};
                this.state[i + '_' + j]['alive'] = (Math.random() > chance);
                this.state[i + '_' + j]['neighbor'] = neighborCells;
            }
        }
         



    }
    checkAlive(cells,cellName) {

        //console.log(neighborCells);
        let o = this.state[cellName];
        //console.log(i,j);
        let neighborCells = o['neighbor'];

        let alivecount = 0;
        for (let cell in neighborCells) {
            //console.log(neighborCells[cell],this.state[neighborCells[cell]]);
            if (this.state[neighborCells[cell]]) {
                if (this.state[neighborCells[cell]]['alive']) {
                    alivecount++;
                }
            }
        }
        //let alive = this.state[i + '_' + j]['alive'];
        //console.log(alive,alivecount);
        if (o['alive']) {
            if (alivecount < 2 || alivecount > 3) {
                o['alive'] = false;
            }
        } else {
            if (alivecount == 3) {
                o['alive'] = true;
            }
        }
        //console.log(o);
        //let cells = {};
        cells[cellName] = {};
        cells[cellName]['alive'] = o['alive'];
        cells[cellName]['neighbor'] = o['neighbor'];
        return cells;       
	}


    tick() {
        //console.log(this.runTime,stopTime);
        if (this.runTime >= stopTime) {
            clearInterval(this.timer);
        }
    
	let newState = {};
        for (let cellName in this.state) {
	    newState = this.checkAlive(newState,cellName);
        }
	this.setState(newState);
	this.runTime++;
     
    }

    componentDidMount() {
        this.timer = setInterval(this.tick, 500);
      
    }

    componentWillUnmount() {
        clearInterval(this.timer);
    }

    render() {
    
        return (
            <div id="show">
            { Object.keys(this.state).map((k, index) => <Cell key={k} alive={this.state[k]['alive']}/>) }
			
			</div>
        );

    }
}

ReactDOM.render(
    <App/>,
    document.getElementById('app')
);
#show {
  width: 400px;
  height: 400px;
  border: 1px solid black;
  xbackground: #f0f0f0;
  margin: 0;
  padding: 0;
}
.life {
  width: 5px;
  height: 5px;
  xborder: 1px solid black;
  background: white;
  float: left;
  xmargin: 1px;
}
.alive {
  background: black;
}
body {
  margin: 0;
  padding: 0;
  xbackground: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Upvotes: 0

Diogo Sgrillo
Diogo Sgrillo

Reputation: 2701

You are calling this.setState for each cell.

Each time you call this.setState, render is called. You should delay this call.

Something like this:

let newState = {};
for (let cellName in this.state) {
    newState = {...newState, this.checkAlive(cellName)}; //remember removing the setState from checkAlive
}
this.setState(newState);

Upvotes: 2

Related Questions