Chef1075
Chef1075

Reputation: 2724

Error in simple project: TypeError: Cannot read property '0' of undefined [React]

I'm trying to build a simple react tic tac toe app, and I've gotten fairly far but now I'm getting an error I've been unable to debug.

I have a feeling it has to do with how my renderPlaySquare(i) method is set up, but after that I'm lost ...

It is that the playSquareStates state is not being set up correctly?

Sorry for the long post and thank you for your help.

I've included all of my code here because it's not very long, as well as the error.

Here are the two errors

Board.renderPlaySquare src/App.js:53

 50 | 
  51 | renderPlaySquare(i) {
  52 |     debugger;
> 53 |     return (
  54 |         <PlaySquare value={this.props.playSquareStates[i]} onClick={() => this.props.onClick(i)}
  55 |         />
  56 |     );

View compiled Board.render src/App.js:66

  63 | <div>
  64 |     {/* Sets the index of the board*/}
  65 |     <div className="board-row">
> 66 |         {this.renderPlaySquare(0)}
  67 |         {this.renderPlaySquare(1)}
  68 |         {this.renderPlaySquare(2)}
  69 |     </div>

And here is my main project

import React, {Component} from 'react';
// import ReactDOM from 'react-dom'
import './index.css'

// Functions for the game

function PlaySquare(props) {
    debugger;
    return (
        <button
            className="playSquare"
            onClick={props.onClick}>
            {props.value}
        </button>
    );
}

// Declare a winner
function whoIsTheWinner(combination) {

    const lines = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],
        [0, 4, 8],
        [2, 4, 6],
    ];

    // Loop through combinations
    for (let i = 0; i < lines.length; i++) {

        const [a, b, c] = lines[i];

        if (combination[a] && combination[a] === combination[b] && combination[a] === combination[c]) {
            return combination[a];
        }
    }
    return null;
}


// Main classes for the game

// The main component of the game, where the
// squares live
class Board extends Component {

    renderPlaySquare(i) {
        debugger;
        return (
            <PlaySquare value={this.props.playSquareStates[i]} onClick={() => this.props.onClick(i)}
            />
        );
    }

    render() {

        // Create the buttons they are going to play on
        return (
            <div>
                {/* Sets the index of the board*/}
                <div className="board-row">
                    {this.renderPlaySquare(0)}
                    {this.renderPlaySquare(1)}
                    {this.renderPlaySquare(2)}
                </div>
                <div className="board-row">
                    {this.renderPlaySquare(3)}
                    {this.renderPlaySquare(4)}
                    {this.renderPlaySquare(5)}
                </div>
                <div className="board-row">
                    {this.renderPlaySquare(6)}
                    {this.renderPlaySquare(7)}
                    {this.renderPlaySquare(8)}
                </div>
            </div>
        );
    }
}

class Game extends Component {

    constructor(props) {
        super(props);
        this.state = {
            history: [{
                playSquareStates: Array(9).fill(null)
            }],
            xIsNext: true
        };
    }

    handleClick(i) {

        const historyAll = this.state.history;
        const currentGame = historyAll[historyAll.length - 1];
        const currentGameConfiguration = currentGame.playSquareStates.slice();


        if (whoIsTheWinner(currentGameConfiguration) || currentGameConfiguration[i]) {
            return;
        }

        currentGameConfiguration[i] = this.state.xIsNext ? 'X' : 'O';

        // Now set the state AND history
        this.setState({
            history: historyAll.concat([{
                playSquareStates: currentGameConfiguration
            }]),
            xIsNext: !this.state.xIsNext,
        });
    }

    render() {

        const historyAll = this.state.history;
        const currentGame = historyAll[historyAll.length - 1];
        const winner = whoIsTheWinner(currentGame.playSquareStates);

        let currentTurn;

        if (winner) {
            currentTurn = 'Winner: ' + winner;
        } else {
            currentTurn = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
        }

        return (
            <div className="game">
                <div className="game-board">
                    <Board
                    playSquares = {currentGame.playSquareStates}
                    onClick = {(i) => this.handleClick(i)}
                />
                </div>
                <div className="game-info">
                    <div>
                        {currentTurn}
                    </div>
                    <ol>
                        {/* TODO */}
                    </ol>
                </div>
            </div>
        );
    }
}

class App extends Component {
    render() {
        return (
            <div className="App">
                <header className="App-header">
                    <h1>Welcome to React my Tic Tac Toe App</h1>
                </header>
                <Game/>
            </div>

        );
    }
}

export default App;

Upvotes: 0

Views: 685

Answers (1)

Claies
Claies

Reputation: 22323

In your Game component, you render your Board component like so:

<Board
  playSquares = {currentGame.playSquareStates}
  onClick = {(i) => this.handleClick(i)}
/>

This creates a prop on the Board component of playSquares, not playSquareStates.

Therefore, you should use:

<PlaySquare value={this.props.playSquares[i]} onClick={() => this.props.onClick(i)}

Upvotes: 2

Related Questions