vsoni
vsoni

Reputation: 497

Reset Box in Tic Tac Toe with React

I am having issue with resetting the game whenever I click on reset button.

I have created tic tac toe game with react hooks with 2 components Game and Box.How can I add reset button with the use of React hooks.

I am Trying to make a with the code below.Also I tried to add reset button in it but is not working as required.Please help me out on how I can use reset button correctly to reset the box to empty.

Here is my Box component.

import React, { useState } from 'react'
import './style.css'

function Box(props) {
    const [text, setText] = useState('')

    function toggleText() {
        if(text === '') {
            setText(props.currentState)
            props.changeTurn(props.row, props.col)
        }
    }
    return <div className="box" onClick={toggleText}>{text}</div>
}

export default Box

Here is my Game Component.

import './style.css'
import Box from '../Box'

const board = [[],[],[]]

function Game() {
    const [turn, setTurn] = useState('X')
    const [winningtext, setWinningText] = useState('')
    console.log(board)
    function changeTurn(row, col) {
        board[row][col] = turn
        setTurn(turn => turn === 'X' ? 'O' : 'X' )
        //console.log(board.length)

        const winner = checkForWin()
        //console.log(winner)
        if(winner) {
            setWinningText(winner + ' Won!')
        }
    }
    // Winning game logic
    function checkForWin() { 
        // row test
        for(let i = 0; i < board.length; i++) {
            const row = board[i]
            //console.log(row[0])
            if(row[0] === row[1] && row[1] === row[2] && row[0]){
                return row[0]
            } 
        }

        //column test
        for(let i = 0; i < board.length; i++) {
            const el1 = board[0][i]
            const el2 = board[1][i]
            const el3 = board[2][i]
            //console.log(`${el1}  ${el2}  ${el3}`)
            if(el1 === el2 && el2 === el3 && el1) {
                return el1
            }
        }
        //diagonal test
        const d1 = board[0][0]
        const d2 = board[1][1]
        const d3 = board[2][2]

        if(d1 === d2 && d2 === d3 && d1) {
            return d1
        }

        const p1 = board[0][2]
        const p2 = board[1][1]
        const p3 = board[2][0]

        if(p1 === p2 && p2 === p3 && p1) {
            return p1
        }
        return false
    }

    function reset() {
        //Clear all grids and winner message
        setWinningText('')

    }

    return <div className="game">
        <h1>Tic Tac Toe</h1><br/>
        <button className="reset" onClick={reset}>Reset</button><br/><br/>
        <div id="winning-text">{winningtext}</div>
        <div className="row row-1">
            <Box row={0} col={0} currentState={turn} changeTurn={changeTurn} />
            <Box row={0} col={1} currentState={turn} changeTurn={changeTurn} />
            <Box row={0} col={2} currentState={turn} changeTurn={changeTurn} />
        </div>

        <div className="row row-2">
            <Box row={1} col={0} currentState={turn} changeTurn={changeTurn} />
            <Box row={1} col={1} currentState={turn} changeTurn={changeTurn} />
            <Box row={1} col={2} currentState={turn} changeTurn={changeTurn} />
        </div>

        <div className="row row-3">
            <Box row={2} col={0} currentState={turn} changeTurn={changeTurn} />
            <Box row={2} col={1} currentState={turn} changeTurn={changeTurn} />
            <Box row={2} col={2} currentState={turn} changeTurn={changeTurn} />
        </div>

    </div>
}

export default Game

Upvotes: 1

Views: 1639

Answers (2)

skovy
skovy

Reputation: 5650

  • In general, if there is "state" (eg: boards) you want that to be within a React component. You could reset boards by setting it to the same initial value (eg: [[], [], []]) but it will not re-render because React won't know it changed. The suggestion I offered below is to move boards to use the useState hook so whenever it changes React can properly re-render.
  • Another issue is that both board and the local value in the Box component were storing similar state so it would be a challenge to keep them in sync. Rather, having a single board value in the parent and passing a callback to the Box will probably make your life easier and avoid weird data synchronization issues.
  • You didn't share the CSS so I added some basic styles. Consider using CSS grid for this (seems like a perfect use case).
  • Consider using a map for each row and column to reduce the need for the duplication (this is simple enough it's not a big deal, but with this, you could easily make it a 5 x 5 grid or something).
  • I made as few changes as possible to get it working, so might have missed some things.

function Box(props) {
  const { value, changeTurn, row, col } = props;

  function toggleText() {
    if (!value) {
      changeTurn(row, col);
    }
  }
  return (
    <div className="box" onClick={toggleText}>
      {value}
    </div>
  );
}

function Game() {
  const [board, setBoard] = React.useState([[], [], []]);
  const [turn, setTurn] = React.useState("X");
  const [winningtext, setWinningText] = React.useState("");
  console.log(board);
  function changeTurn(row, col) {
    const newBoard = [...board];
    const newRow = [...board[row]];
    newBoard[row] = newRow;
    newBoard[row][col] = turn;
    setBoard(newBoard);

    setTurn(turn => (turn === "X" ? "O" : "X"));
    //console.log(board.length)

    const winner = checkForWin();
    //console.log(winner)
    if (winner) {
      setWinningText(winner + " Won!");
    }
  }
  // Winning game logic
  function checkForWin() {
    // row test
    for (let i = 0; i < board.length; i++) {
      const row = board[i];
      //console.log(row[0])
      if (row[0] === row[1] && row[1] === row[2] && row[0]) {
        return row[0];
      }
    }

    //column test
    for (let i = 0; i < board.length; i++) {
      const el1 = board[0][i];
      const el2 = board[1][i];
      const el3 = board[2][i];
      //console.log(`${el1}  ${el2}  ${el3}`)
      if (el1 === el2 && el2 === el3 && el1) {
        return el1;
      }
    }
    //diagonal test
    const d1 = board[0][0];
    const d2 = board[1][1];
    const d3 = board[2][2];

    if (d1 === d2 && d2 === d3 && d1) {
      return d1;
    }

    const p1 = board[0][2];
    const p2 = board[1][1];
    const p3 = board[2][0];

    if (p1 === p2 && p2 === p3 && p1) {
      return p1;
    }
    return false;
  }

  function reset() {
    setBoard([[], [], []]);
    setWinningText("");
  }

  return (
    <div className="game">
      <h1>Tic Tac Toe</h1>
      <br />
      <button className="reset" onClick={reset}>
        Reset
      </button>
      <br />
      <br />
      <div id="winning-text">{winningtext}</div>
      <div className="boxes">
        {[0, 1, 2].map(row => {
          return [0, 1, 2].map(col => {
            return (
              <Box
                row={row}
                col={col}
                key={`${row}-${col}`}
                value={board[row][col]}
                currentState={turn}
                changeTurn={changeTurn}
              />
            );
          });
        })}
      </div>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<Game />, rootElement);
.boxes {
  display: grid;
  grid-template-columns: 32px 32px 32px;
}

.box {
  border: 1px solid red;
  width: 32px;
  height: 32px;
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

Upvotes: 1

Phoenix1355
Phoenix1355

Reputation: 1652

You could make the box component stateless, so you move the state logic from each box component out of it into the game component instead. You already have an array for the whole board, so why not just pass the state from the board to each box instead? Then you can also reset the board array, which would reset all boxes.

Check the following example. This hasn't been tested, but think it would work.

import React from 'react'
import './style.css'

function Box(props) {
    function onClick() {
        props.changeTurn(props.row, props.col)
    }

    return <div className="box" onClick={onClick}>{props.currentState}</div>
}

export default Box
import './style.css'
import Box from '../Box'

const board = [[],[],[]];

function Game() {
    const [turn, setTurn] = useState('X')
    const [winningtext, setWinningText] = useState('')
    console.log(board)
    function changeTurn(row, col) {
        board[row][col] = turn
        setTurn(turn => turn === 'X' ? 'O' : 'X' )
        //console.log(board.length)

        const winner = checkForWin()
        //console.log(winner)
        if(winner) {
            setWinningText(winner + ' Won!')
        }
    }
    // Winning game logic
    function checkForWin() { 
        // row test
        for(let i = 0; i < board.length; i++) {
            const row = board[i]
            //console.log(row[0])
            if(row[0] === row[1] && row[1] === row[2] && row[0]){
                return row[0]
            } 
        }

        //column test
        for(let i = 0; i < board.length; i++) {
            const el1 = board[0][i]
            const el2 = board[1][i]
            const el3 = board[2][i]
            //console.log(`${el1}  ${el2}  ${el3}`)
            if(el1 === el2 && el2 === el3 && el1) {
                return el1
            }
        }
        //diagonal test
        const d1 = board[0][0]
        const d2 = board[1][1]
        const d3 = board[2][2]

        if(d1 === d2 && d2 === d3 && d1) {
            return d1
        }

        const p1 = board[0][2]
        const p2 = board[1][1]
        const p3 = board[2][0]

        if(p1 === p2 && p2 === p3 && p1) {
            return p1
        }
        return false
    }

    function reset() {
        //Clear all grids and winner message
        const board = [[],[],[]];
    }

    return <div className="game">
        <h1>Tic Tac Toe</h1><br/>
        <button className="reset" onClick={reset}>Reset</button><br/><br/>
        <div id="winning-text">{winningtext}</div>
        <div className="row row-1">
            <Box row={0} col={0} currentState={board[0][0]} changeTurn={changeTurn} />
            <Box row={0} col={1} currentState={board[0][1]} changeTurn={changeTurn} />
            <Box row={0} col={2} currentState={board[0][2]} changeTurn={changeTurn} />
        </div>

        <div className="row row-2">
            <Box row={1} col={0} currentState={board[1][0]} changeTurn={changeTurn} />
            <Box row={1} col={1} currentState={board[1][1]} changeTurn={changeTurn} />
            <Box row={1} col={2} currentState={board[1][2]} changeTurn={changeTurn} />
        </div>

        <div className="row row-3">
            <Box row={2} col={0} currentState={board[2][0]} changeTurn={changeTurn} />
            <Box row={2} col={1} currentState={board[2][1]} changeTurn={changeTurn} />
            <Box row={2} col={2} currentState={board[2][2]} changeTurn={changeTurn} />
        </div>

    </div>
}

export default Game

Upvotes: 1

Related Questions