I’m building a tic-tac-toe game and able to have two players play against each other. Now I want to simulate randomness on Play O. Part of the randomness, is getting one of the 9 children components, "PositionedComponent", to simulate the computer doing the click and have it change the display.
I thought using useRef() and .click() would allow for this simulation. I created an array of useRef() and in each PositionedComponent, I give them each a useRef(). Then inside of useEffect() I thought of creating a random number and then call the click function in autoClick[randomValue].current.click()
I always get an undefined value for autoClick but I’m not sure why when I initialized it at the top.
I’ve also tried declaring the array as autoClick = useState(Array(9).fill(useRef())) as well as doing autoClick = [useRef()…,useRef()] and still the same undefined value issue.
Another method I tried using was document.getElementById("") but still the same undefined value issue. I’m not sure what is the issue since I’m declaring the autoClick at the top but still gettin undefined value. Thank you for any advice.
function TicTacToeGrid()
{
// Your tic-tac-toe logic goes here
const [playersMove, setPlayersMove] = useState(true);
const [symbol, setSymbol] = useState('');
const [currPosition, setCurrPosition] = useState(23)
const [board, setBoard] = useState(Array(9).fill('-'));
const [game, setGame] = useState(false)
const [tie, setTie] = useState(false)
const autoClick = []
for(let i = 0; i < 9; i++)
{
autoClick[i] = useRef();
}
const handlePlayersTurn = async (position) =>
{
console.log("auto click is", autoClick)
if(playersMove)
{
setSymbol('X')
}
else
{
setSymbol('O')
}
setPlayersMove(!playersMove)
setCurrPosition(position)
};
const positon1 = <PositionedComponent x="0px" y = "0px" ref={autoClick[0]} playersMove={playersMove} handlePlayersTurn={() => handlePlayersTurn(1)}/>
const positon2 = <PositionedComponent x="210px" y = "0px" ref={autoClick[1]} playersMove={playersMove} handlePlayersTurn={() =>handlePlayersTurn(2)}/>
const positon6 = <PositionedComponent x="420px" y = "0px" ref={autoClick[2]} playersMove={playersMove} handlePlayersTurn={() =>handlePlayersTurn(3)}/>
const positon3 = <PositionedComponent x="0px" y = "210px" ref={autoClick[3]} playersMove={playersMove} handlePlayersTurn={() =>handlePlayersTurn(4)}/>
const positon4 = <PositionedComponent x="210px" y = "210px" ref={autoClick[4]} playersMove={playersMove} handlePlayersTurn={() =>handlePlayersTurn(5)}/>
const positon7 = <PositionedComponent x="420px" y = "210px" ref={autoClick[5]} playersMove={playersMove} handlePlayersTurn={() =>handlePlayersTurn(6)}/>
const positon5 = <PositionedComponent x="0px" y = "420px" ref={autoClick[6]} playersMove={playersMove} handlePlayersTurn={() =>handlePlayersTurn(7)}/>
const positon9 = <PositionedComponent x="210px" y = "420px" ref={autoClick[7] playersMove={playersMove} handlePlayersTurn={() =>handlePlayersTurn(8)}/>
const positon8 = <PositionedComponent x="420px" y = "420px" ref={autoClick[8]} playersMove={playersMove} handlePlayersTurn={() =>handlePlayersTurn(9)}/>
useEffect(()=>
{
if(playersMove)
{
console.log("X turn")
}
else
{
console.log("O turn")
const randomValue = Math.floor(Math.random() * 10);
autoClick[randomValue].current.click()
}
const newBoard = board
newBoard[currPosition-1] = symbol
const finalBoard = newBoard
setBoard(finalBoard);
setGame(isWinner(finalBoard, symbol));
setTie(catScratch(board))
})
3
Answers
The
autoClick
was an initialised empty array in the component body, not triggering re-renders. This leads to undefined refs in theuseEffect
.try:
The issue you are facing with
autoClick
being undefined is due to the fact that you are trying to accessautoClick
in theuseEffect
function, and at that point,autoClick
is not defined within the scope of theuseEffect
. To fix this issue, you should declareautoClick
using theuseState
hook, and you can also use theuseRef
hook to create the array of refs. Here’s an updated version of your code:In React everything is controlled by state / props. Instead of simulating a click, just change the state directly by calling
handlePlayersTurn(position)
.In addition, you have too much states. If you need to derive a value (
symbol
fromplayerMove
for example), you don’t need a different state, just compute it on the fly.