在React中使用子组件模拟onClick调用

eh57zj3b  于 2023-11-21  发布在  React
关注(0)|答案(2)|浏览(132)

我正在构建一个井字游戏,并且能够让两个玩家互相玩。现在我想在Play O上模拟随机性。随机性的一部分是让9个子组件之一“PositionedComponent”模拟计算机进行点击并让它改变显示。
我认为使用useRef()和.click()可以实现这种模拟。我创建了一个useRef()数组,在每个PositionedComponent中,我给予它们一个useRef()。然后在useEffect()中,我想到创建一个随机数,然后在autoClick[randomValue]. current.click()中调用click函数。
我总是为autoClick得到一个未定义的值,但我不知道为什么当我在顶部初始化它时。
我还尝试将数组声明为autoClick = useState(Array(9).fill(useRef(),以及执行autoClick = [useRef()...,useRef()],但仍然存在未定义值的问题。
我尝试使用的另一个方法是document.getElementById(“”),但仍然是相同的未定义值问题。我不知道是什么问题,因为我在顶部声明了autoClick,但仍然得到未定义值。谢谢你的任何建议。

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))
  })

字符串

ruoxqz4g

ruoxqz4g1#

autoClick在组件体中是一个初始化的空数组,不会触发重新呈现。这导致useEffect中的未定义引用。
尝试:

const autoClick = Array(9).fill(null).map(() => useRef());

字符串

yc0p9oo0

yc0p9oo02#

在React中,一切都由state / props控制。而不是模拟单击,只需通过调用handlePlayersTurn(position)直接更改状态。
另外,你有太多的状态。如果你需要从playerMove中得到一个值(例如symbol),你不需要一个不同的状态,只需要在运行中计算它。

备注:这个例子不包括获胜/平局条件。2当没有更多的单元格时,useEffect将被跳过。3另外,只有打开的单元格才会有点击处理程序。

const { useState, useEffect } = React

const initGame = () => Array.from({ length: 9 }, (_, i) => ({
  position: i,
}))

function TicTacToeGrid() {
  const [playersMove, setPlayersMove] = useState(true)
  const [board, setBoard] = useState(initGame)
  
  const handlePlayersTurn = position => {
    // compute the symbol when you need it
    const value = playersMove ? 'X' : 'O'
    
    // update the board using the previous board state
    setBoard(b => b.toSpliced(position, 1, {
      position,
      value
    }))
    
    // update the current player
    setPlayersMove(p => !p)
  }
  
  // compute available positions
  const availablePositions = board.filter(c => !c.value)
    
  useEffect(() => {
    // if it's the player's turn or no more positions, skip
    if(playersMove || availablePositions.length === 0) return
    
    // find an open position
    const idx = Math.floor(Math.random() * availablePositions.length)
    
    // call handlePlayersTurn with open position found
    handlePlayersTurn(availablePositions[idx].position)
  }, [playersMove, availablePositions])
  
  return (
    <div className="board">
      {board.map(({ position, value }) => (
        <div
          {...!value && { onClick: () => handlePlayersTurn(position) }}
          className="cell" 
          key={position}>
          {value || '-'}
        </div>
      ))}
    </div>
  )
};

ReactDOM
  .createRoot(root)
  .render(<TicTacToeGrid />)
.board {
  display: grid;
  height: 50vmin;
  width: 50vmin;
  grid-template-rows: repeat(3, 1fr);
  grid-template-columns: repeat(3, 1fr);
}

.cell {
  display: flex;
  border: 1px solid black;
  justify-content: center;
  align-items: center;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

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

相关问题