我如何将不同文件中的 React 组件分开以保持其正常工作并避免 TypeErrors?

How can I separate React components in different files keeping it working and avoiding TypeErrors?

我正在实现 reactjs.org 教程,这是一个井字游戏,但我正在尝试使用挂钩而不是本教程中使用的 类。

这是我在单个文件中编写的代码,运行良好:

function Square(props) {
    return (
      <button className="square" onClick={props.onClick}>
        {props.value}
      </button>
    );
  }
  
  function Board(props) {
    
    const renderSquare = (i) => {
      return (
        <Square
          value={props.squares[i]}
          onClick={() => props.onClick(i)}
        />
      );
    }

      return (
        <div>
          <div className="board-row">
            {renderSquare(0)}
            {renderSquare(1)}
            {renderSquare(2)}
          </div>
          <div className="board-row">
            {renderSquare(3)}
            {renderSquare(4)}
            {renderSquare(5)}
          </div>
          <div className="board-row">
            {renderSquare(6)}
            {renderSquare(7)}
            {renderSquare(8)}
          </div>
        </div>
      );
    }
  
  function Game() {
    
    const [clickedSquares, setClickedSquares] = useState(Array(9).fill(null))
    const [xIsNext, setXIsNext] = useState(true)
    const [moves, setMoves] = useState(0)
    const [playsHistory, setPlaysHistory] = useState([{
        history: {
          squares: Array(9).fill(null)
        },
        xIsNext: true
      }])
      const index = 0

    const determineWinner = (squares) => {
    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],
    ];
    for (let i = 0; i < lines.length; i++) {
        const [a, b, c] = lines[i];
        if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
        return squares[a];
        }
    }
    return null;
    }  

    const handleClick = (i) => {
        const squares = clickedSquares.slice()
        if (determineWinner(squares) || squares[i]) return
        squares[i] = xIsNext ? 'X' : 'O';
        // console.log(xIsNext)
        setXIsNext(!xIsNext)
        // console.log(xIsNext)

        let current = playsHistory
        current ={
            history:{
                squares: squares},
            xIsNext: !xIsNext     
        }
        // console.log(current)
        setClickedSquares(squares)
        setMoves(prevMoves => prevMoves + 1)
        setPlaysHistory(prevPlays => ([...prevPlays, current]))
        console.log(playsHistory)
    }
    
    let winner =  determineWinner(clickedSquares)
    let status = winner ? `Winner: ${winner}` : `Next player: ${xIsNext ? 'X' : 'O'}`
    if(!winner && moves === 9) status = 'Draw'
 
      return (
        <div className="game">
          <div className="game-board">
            <Board
              squares={clickedSquares}
              onClick={(i) => handleClick(i)}
            />
          </div>
          <div className="game-info">
            <div>{status}</div>
            <ol>{/* TODO */}</ol>
          </div>
        </div>
      );
  }

  export default Game

我已经完成了大部分功能,但是我一直在处理单个文件。因此,我尝试重构我的项目,将其功能划分为每个组件的较小文件。我创建了三个组件文件:

Square.jsx:

function Square(props) {
return (
  <button className="square" onClick={props.onClick}>
    {props.value}
  </button>
);
}

export default Square

Board.jsx:

function Board(props) {

const renderSquare = (i) => {
  return (
    <Square
      value={props.squares[i]}
      onClick={() => props.onClick(i)}
    />
  );
}

  return (
    <div>
      <div className="board-row">
        {renderSquare(0)}
        {renderSquare(1)}
        {renderSquare(2)}
      </div>
      <div className="board-row">
        {renderSquare(3)}
        {renderSquare(4)}
        {renderSquare(5)}
      </div>
      <div className="board-row">
        {renderSquare(6)}
        {renderSquare(7)}
        {renderSquare(8)}
      </div>
    </div>
  );
}
export default Board;

Game.jsx:

function Game() {
    
    const [clickedSquares, setClickedSquares] = useState(Array(9).fill(null))
    const [xIsNext, setXIsNext] = useState(true)
    const [moves, setMoves] = useState(0)
    const [playsHistory, setPlaysHistory] = useState([{
        history: {
          squares: Array(9).fill(null)
        },
        xIsNext: true
      }])

    const determineWinner = (squares) => {
    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],
    ];
    for (let i = 0; i < lines.length; i++) {
        const [a, b, c] = lines[i];
        if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
        return squares[a];
        }
    }
    return null;
    }  

    const handleClick = (i) => {
        const squares = clickedSquares.slice()
        squares[i] = xIsNext ? 'X' : 'O';
        // console.log(xIsNext)
        setXIsNext(!xIsNext)
        // console.log(xIsNext)

        let current = playsHistory
        current ={
            history:{
                squares: squares},
            xIsNext: !xIsNext     
        }
        // console.log(current)
        setClickedSquares(squares)
        setMoves(prevMoves => prevMoves + 1)
        setPlaysHistory(prevPlays => ([...prevPlays, current]))
        console.log(playsHistory)
        if (determineWinner(squares) || squares[i]) return
    }
    
    let winner =  determineWinner(clickedSquares)
    let status = winner ? `Winner: ${winner}` : `Next player: ${xIsNext ? 'X' : 'O'}`
    if(!winner && moves === 9) status = 'Draw'
 
      return (
        <div className="game">
          <div className="game-board">
            <Board
              squares={clickedSquares}
              onClick={(i) => handleClick(i)}
            />
          </div>
          <div className="game-info">
            <div>{status}</div>
            <ol>{/* TODO */}</ol>
          </div>
        </div>
      );
  }

export default Game;

现在,我在 Board 组件的这一点收到 TypeError:Cannot read property '0' of undefined 错误:

   7 | const renderSquare = (i) => {
   8 |   return (
   9 |  <Square
> 10 |    value={props.squares[i]}
     | ^  11 |    onClick={() => props.onClick(i)}
  12 |  />
  13 |   );

如果我在单个 .jsx 文件中设置相同的代码,效果很好。我已经从第 10 行的 props.squares 中删除了 [i],但是出现了新的错误 TypeError: props.onClick is not a function。我认为 Board.jsx 文件无法识别我从 Game.jsx 文件传递​​的参数。我应该如何将我的项目组件化到不同的文件中并使其正常运行?

分离组件时必须非常小心。看看这个 CodeSandbox link。我没有对你的逻辑做任何改变。有代码改进的余地,但这不在要求中,所以我刚刚将您的文件分成单独的组件文件。