3
\$\begingroup\$

I think the general functionality is there, but sometimes it looks a little slow/clunky when I try to run my app. Highlighting cells can be slow as it skips over some of the cells I hover over if my mouse is moving somewhat fast. Also the speed the grid evolves doesn't always consistent.

I suspect that calling setState() so often is a bit heavy? I was also wondering if there are "better" ways to do the styling of the grid.

https://jsfiddle.net/bonbonlemon/49jzdo1n/

JSX:

class GameOfLife extends React.Component {
  constructor(props) {
    super(props);
    const grid = this.createNewGrid();

    this.state = {
        grid: grid,
      isPointerDown: false,
      pointerStatus: false,
      isStarted: false,
      intervalId: 0,
      ticksPerSecond: 10
    };

    this.updateCell = this.updateCell.bind(this);
    this.handlePointerDown = this.handlePointerDown.bind(this);
    this.handlePointerOver = this.handlePointerOver.bind(this);
    this.handlePointerUp = this.handlePointerUp.bind(this);
    this.evolve = this.evolve.bind(this);
    this.clearGrid = this.clearGrid.bind(this);
    this.updateTicksPerSecond = this.updateTicksPerSecond.bind(this);
    this.toggleStart = this.toggleStart.bind(this);
  }

  createNewGrid() {
    let grid = [];
        for (let row = 0; row < 40; row++) {
        grid.push([]);
        for (let col = 0; col < 40; col++) {
        grid[row].push(false);
      }
    }

    return grid;
  }

  updateCell(e, updatePointerDown) {
    let coordinates = e.currentTarget.getAttribute('coordinates');
    coordinates = coordinates.split(",");
    coordinates = coordinates.map(coordinate => parseInt(coordinate));
    const [row, col] = coordinates;

    let grid = this.state.grid;
    let cell = grid[row][col];

    let { isPointerDown, pointerStatus } = this.state;
    if (updatePointerDown) {
        isPointerDown = true;
      pointerStatus = !cell;
    }

        grid[row][col] = pointerStatus;

    this.setState({
        grid: grid,
      isPointerDown: isPointerDown,
      pointerStatus: pointerStatus
    });
  }

  handlePointerDown(e) {
    e.preventDefault();
    this.updateCell(e, true);
  }

  handlePointerOver(e) {
    e.preventDefault();
    if (this.state.isPointerDown) {
        this.updateCell(e);
    }
  }

  handlePointerUp(e) {
    e.preventDefault();
    this.setState({isPointerDown: false});
  }

  evolve(e) {
    if (e) { e.preventDefault(); }
    let currentGen = this.state.grid;
    let nextGen = [];

    currentGen.forEach((row, rowIdx) => {
        nextGen.push([]);
        row.forEach((cell, colIdx) => {
        const numNeighbors = this.countNeighbors(rowIdx, colIdx);
        if (currentGen[rowIdx][colIdx]) {
            if (numNeighbors >= 2 && numNeighbors <= 3) {
            nextGen[rowIdx].push(true);
          } else {
            nextGen[rowIdx].push(false);
          }
        } else {
            if (numNeighbors == 3) {
            nextGen[rowIdx].push(true);
          } else {
            nextGen[rowIdx].push(false);
          }
        }
      });
    });

    this.setState({grid: nextGen});
  }

  countNeighbors(rowIdx, colIdx) {
    const { grid } = this.state;
    let numNeighbors = 0;
    for (let dy = -1; dy <= 1; dy++) {
        if (grid[rowIdx + dy] ) {
        for (let dx = -1; dx <= 1; dx++) {
          if (grid[rowIdx + dy][colIdx + dx] && (dy != 0 || dx != 0)) {
            numNeighbors++;
          }
            }
      }
    }
    return numNeighbors;
  }

  clearGrid(e) {
    e.preventDefault();
    const grid = this.createNewGrid();
    this.setState({grid: grid});
  }

  toggleStart(e) {
    let { isStarted, ticksPerSecond, intervalId } = this.state;

    if (!isStarted) {
        intervalId = setInterval(this.evolve, 1000 / ticksPerSecond);
    } else {
        clearInterval(intervalId);
    }

    this.setState({ 
        isStarted: !isStarted,
      intervalId: intervalId
    });
  }

  updateTicksPerSecond(e) {
    this.setState({ticksPerSecond: e.currentTarget.value});
  }

  render() {
    const { grid, ticksPerSecond, isStarted } = this.state;

    return (
        <div>
        <div id="grid" onPointerUp={this.handlePointerUp}>
          {
            grid.map((row, rowIdx) => (
              <div className="row" key={rowIdx.toString()}>
                {row.map((cell, colIdx) => (
                  <div className={"cell " + (cell ? "alive": "dead")} onPointerDown={this.handlePointerDown} onPointerOver={this.handlePointerOver} coordinates={[rowIdx, colIdx]} key={[rowIdx, colIdx]} />
                ))}
              </div>
            ))
          }
        </div>
        <button onClick={this.clearGrid}>Clear</button>
        <button onClick={this.evolve}>Next</button>
        <button onClick={this.toggleStart}>{ isStarted ? "Pause" : "Start" }</button>
        <input
          type="text" 
          value={ticksPerSecond}
          onChange={this.updateTicksPerSecond}  
        />
      </div>
    )
  }
}

ReactDOM.render(<GameOfLife />, document.querySelector("#app"))
\$\endgroup\$

closed as unclear what you're asking by Toby Speight, Stephen Rauch, yuri, Dannnno, Raystafarian Aug 29 '18 at 5:38

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 2
    \$\begingroup\$ Welcome to Code Review! This question is incomplete. To help reviewers give you better answers, please add sufficient context to your question. The more you tell us about what your code does and what the purpose of doing that is, the easier it will be for reviewers to help you. The current title states your concerns about the code; it needs an edit to simply state the task; see How to get the best value out of Code Review: Asking Questions for guidance on writing good question titles. \$\endgroup\$ – Toby Speight Aug 27 '18 at 7:38