Reputation: 615
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:
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
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
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