skip to Main Content

I am trying to make a tic tac toe game using reactjs. I have a function named setSquareValue to set the value of squares to X but when I am clicking on a square all the other squares also filling with X.

The hierarchy is Game.js -> Board.js -> Square.js. I am using useState() which is in Game.js and also the function setSquareValue is in Game.js. I am passign the function to Square.js through Board.js.

Here is my codes:
Game.js

import React, { useState } from 'react';
import './Game.css'
import Board from './Board';

const Game = () => {
  const [value, setValue] = useState(null);
  const setSquareValue = () => {
    setValue("X")
  }
  return (
    <div className='game'>
      <h1 style={{color: "#fff"}}>Tic Tac Toe</h1>
      <h4 style={{color: "#2D033B"}}>Winner: </h4>
      <Board value={value} setSquareValue={setSquareValue} />
    </div>
  );
};

export default Game;

Board.js

import React from "react";
import Square from "./Square";
import "./Board.css";

const Board = ({ value, setSquareValue }) => {
  const squareKeys = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  return (
    <div className="board">
      {squareKeys.map(squareKey => (
        <Square 
        key={squareKey} 
        value={value} 
        setSquareValue={setSquareValue} />
      ))}
    </div>
  );
};

export default Board;

Square.js

import React from "react";
import "./Square.css";

const Square = ({ value, setSquareValue }) => {
  return (
    <div className="square" onClick={() => setSquareValue()}>
      {value}
    </div>
  );
};

export default Square;

I want, when I click a certain square, only that square fill with X. But every square is filling with X. How can I solve this?

I tried but X is appearing in every box of the game.

Edited: Added CSS codes

Game.css

.game {
  background-color: #AD7BE9;
  width: 100%;
  height: 100vh;
  padding: 2rem;
  display: flex;
  flex-direction: column;
  align-items: center;
}

Board.css

.board {
  background-color: #645CBB;
  color: #fff;
  padding: 1rem;
  margin-top: 1rem;
  border-radius: 5px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

Square.css

.square {
  border: 1px solid #fff;
  height: 5rem;
  width: 5rem;
  font-size: 20px;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
}

4

Answers


  1. You are passing the value to every single square in your map function, for the solution you can move your value state and setSquareValue function to square component so that every square itself has its own value.

    updated Square.js:

    import React from "react";
    import "./Square.css";
    
    const Square = () => {
    const [value, setValue] = useState(null);
      const setSquareValue = () => {
        setValue("X")
      }
      return (
        <div className="square" onClick={setSquareValue}>
          {value}
        </div>
      );
    };
    
    export default Square;
    

    also you don’t need to pass value and setSquareValue from Game.js to Board.js and Square.js anymore;

    Login or Signup to reply.
  2. The problem is where you are tracking value. Move the logic to store the value state from Game.js to Square.js

    When your app begins, there is one instance of Game which has an initial value set to value. Then you pass this single value to Board, where it renders that same value 9 times. Likewise, when you change that single value, it gets changed in Game, then passed to Board, then finally rendered 9 times.

    Let Square store its own state.

    Login or Signup to reply.
  3. You are creating a unique state for all of the square in Game component. so if you change any time all of the square state will change so this problem occurs. So what you can do is instead of common state, you can create an individual state in Square component as:

    CODESANDBOX LINK

    const Square = () => {
      const [value, setValue] = useState(null);
      const setSquareValue = () => {
        setValue("X");
      };
      return (
        <div className="square" onClick={setSquareValue}>
          {value}
        </div>
      );
    };
    
    Login or Signup to reply.
  4. It is because you are using only one state variable for all square:
    const [value, setValue] = useState(null);

    You can declare as array variable that length is 9 for 9 square.

    Game.js

    import React, { useState } from 'react';
    import './Game.css'
    import Board from './Board';
    
    const Game = () => {
      const [value, setValue] = useState(Array(9).fill(''));
      const setSquareValue = (index) => {
        let values = [...value];
        values[index] = 'X';
        setValue(values);
      }
      return (
        <div className='game'>
          <h1 style={{color: "#fff"}}>Tic Tac Toe</h1>
          <h4 style={{color: "#2D033B"}}>Winner: </h4>
          <Board value={value} setSquareValue={setSquareValue} />
        </div>
      );
    };
    
    export default Game;
    

    Board.js

    import React from "react";
    import Square from "./Square";
    import "./Board.css";
    
    const Board = ({ value, setSquareValue }) => {
      const squareKeys = [1, 2, 3, 4, 5, 6, 7, 8, 9];
      return (
        <div className="board">
          {squareKeys.map((squareKey, index) => (
            <Square 
            key={squareKey} 
            value={value[index]}
            ind = {index}
            setSquareValue={setSquareValue} />
          ))}
        </div>
      );
    };
    
    export default Board;
    

    Square.js

    import React from "react";
    import "./Square.css";
    
    const Square = ({ value, ind, setSquareValue }) => {
      return (
        <div className="square" onClick={() => setSquareValue(ind)}>
          {value}
        </div>
      );
    };
    
    export default Square;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search