如何将 React 组件分离到不同的文件中以使其正常工作并避免 TypeErrors?

Posted

技术标签:

【中文标题】如何将 React 组件分离到不同的文件中以使其正常工作并避免 TypeErrors?【英文标题】:How can I separate React components in different files keeping it working and avoiding TypeErrors? 【发布时间】:2021-08-12 09:07:08 【问题描述】:

我正在实现 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 文件传递​​的参数。我应该如何将我的项目组件化到不同的文件中并保持其正常工作?

【问题讨论】:

【参考方案1】:

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

【讨论】:

以上是关于如何将 React 组件分离到不同的文件中以使其正常工作并避免 TypeErrors?的主要内容,如果未能解决你的问题,请参考以下文章

jquery-select2 - 将 select2 添加到现有的下拉代码中以使其可搜索

为啥 React 需要这么多 node_modules?如何限制加载所有这些文件以使其值得使用 React?

在 React 中将状态数据从一个组件传递到另一个组件

如何将自定义适配器添加到活动中以使列表出现在活动中?

将状态和函数导入 React 组件

如何测试函数是不是使用 async 关键字调用