reactjs React挂钩:两个可以控制彼此状态的同级组件

nom7f22z  于 2022-12-12  发布在  React
关注(0)|答案(1)|浏览(164)

我一直在编写一个chess应用程序,以便帮助自己快速掌握React中引入的钩子。一个用于棋盘本身,另一个用于移动历史记录,它允许您恢复到上一步。当我尝试使用Board组件中的回调将移动传递到移动历史记录时,我得到一个错误Cannot update a component ('App') while rendering a different component ('MoveHistory')。我理解这个错误,但我不完全确定应该如何处理它。
我的组件(减去所有我确信不相关的部分)如下:
应用程序tsx(父组件)

...
const STARTING_FEN = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'

function App() {
  const [FEN, setFEN] = useState(STARTING_FEN);
  const [moveHistory, setMoveHistory] = useState<string[]>([]);
  const [fenHistory, setFenHistory] = useState<string[]>([]);

  // rewind game state to specified move index
  function onRewind(target: number) {
    setFEN(fenHistory[target]);
    setMoveHistory(moveHistory.slice(0, target));
    setFenHistory(fenHistory.slice(0, target));
  }

  // add a new move to the move history
  function onMove(move: string, FEN: string) {
    setMoveHistory([...moveHistory, move]);
    setFenHistory([...fenHistory, FEN]);
  }

  return (
    <div className='app'>
      <Board FEN={FEN} onMove={onMove} />
      <MoveHistory moves={moveHistory} onRewind={onRewind} />
    </div>
  );
}

export default App;

Board.tsx(同级组件1)

...
interface BoardProps {
  FEN: string;
  onMove: Function;
}

function Board(props: BoardProps) {
  const splitFEN = props.FEN.split(' ');
  const [squares, setSquares] = useState(generateSquares(splitFEN[0]));
  const [lastClickedIndex, setLastClickedIndex] = useState(-1);
  const [activeColor, setActiveColor] = useState(getActiveColor(splitFEN[1]));
  const [castleRights, setCastleRights] = useState(getCastleRights(splitFEN[2]));
  const [enPassantTarget, setEnPassantTarget] = useState(getEnPassantTarget(splitFEN[3]));
  const [halfMoves, setHalfMoves] = useState(parseInt(splitFEN[4]));
  const [fullMoves, setFullMoves] = useState(parseInt(splitFEN[5]));

  ...

  // handle piece movement (called when a user clicks on a square)
  function onSquareClicked(index: number) {
    ... // logic determining where to move the piece
    {      
      props.onMove(moveName, getFEN())
    }
  }

  ...

  // get the FEN string for the current board
  function getFEN(): string {
    ... //logic converting board state to strings
    return `${pieceString} ${activeString} ${castleString} ${enPassantString} ${halfMoves} ${fullMoves}`;
  }

  return (
    <div className='board'>
      {[...Array(BOARD_SIZE)].map((e, rank) => {
        return (
          <div key={rank} className='row'>
            {squares.slice(rank * BOARD_SIZE, BOARD_SIZE + rank * BOARD_SIZE).map((square, file) => {
              return (
                <Square
                  key={file}
                  index={coordsToIndex(rank, file)}
                  pieceColor={square.pieceColor}
                  pieceType={square.pieceType}
                  style={square.style}
                  onClick={onSquareClicked}
                />
              );
            })}
          </div>
        )
      })};
    </div>
  );
}
  
export default Board;

MoveHistory.tsx(同层级元件#2)

...
interface MoveHistoryProps {
  moves: string[],
  onRewind: Function;
}

function MoveHistory(props: MoveHistoryProps) {
  return (
    <div className='move-history'>
      <div className='header'>
        Moves
      </div>
      <div className='move-list'>
        {_.chunk(props.moves, 2).map((movePair: string[], index: number) => {
          return (
            <div className='move-pair' key={index}>
              <span>{`${index + 1}.`}</span>
              <span onClick={props.onRewind(index * 2)}>{movePair[0]}</span>
              <span onClick={props.onRewind(index * 2 + 1)}>{movePair[1] || ""}</span>
            </div>
          )
        })}
      </div>
    </div>
  )
}
  
export default MoveHistory;

我已经看了很多其他的stackoverflow问题,它们似乎回答了我在这里提出的问题,但对我来说,我似乎已经在做那里建议的事情了,所以我不确定有什么区别。我也看到了一些使用Redux的建议,我不反对,但如果可以避免的话,那就太好了。

iyr7buue

iyr7buue1#

问题是你在MoveHistory的渲染中调用了props.onRewind
1.应用程序开始渲染

  1. MoveHistory开始呈现,并调用onRewind
    1.在onRewind中,您调用了应用程序中的各种useState setter方法。应用程序尚未完成渲染,但正在调用其状态修改方法。这就是触发错误的原因。
    我想你的意思是这样的:
...
interface MoveHistoryProps {
  moves: string[],
  onRewind: Function;
}

function MoveHistory(props: MoveHistoryProps) {
  return (
    <div className='move-history'>
      <div className='header'>
        Moves
      </div>
      <div className='move-list'>
        {_.chunk(props.moves, 2).map((movePair: string[], index: number) => {
          return (
            <div className='move-pair' key={index}>
              <span>{`${index + 1}.`}</span>
              <span onClick={() => props.onRewind(index * 2)}>{movePair[0]}</span>
              <span onClick={() => props.onRewind(index * 2 + 1)}>{movePair[1] || ""}</span>
            </div>
          )
        })}
      </div>
    </div>
  )
}
  
export default MoveHistory;

请注意,不是调用props.onRewind,而是为它提供一个方法,当单击span时,该方法将调用onRewind

相关问题