LoginSignup
1
0

React入門9: タイムトラベル(1)

Last updated at Posted at 2023-12-10

はじめに

私自身の復習兼、備忘録的な意味もあり、複数回の記事に渡って React を用いてマルバツゲーム(三目並べ)を開発していきたいと思います。

シリーズの一覧

  1. React入門1: 環境構築 [オンライン版]
  2. React入門2: 盤面の作成
  3. React入門3: インタラクションの実装
  4. React入門4: リファクタリング [リフトアップ編]
  5. React入門5: リファクタリング [インタラクション編]
  6. React入門6: 手番の実装
  7. React入門7: ゲームの勝利判定
  8. React入門8: テキストの実装
  9. React入門9: タイムトラベル(1) (今回)
  10. React入門10: タイムトラベル(2)
  11. React入門11: タイムトラベル(3)

目的について

全体の目的

React公式のチュートリアルで公開されているマルバツゲームを 3x3 のマスで実装していきます。

今回の目的

前回までの記事でゲームシステムを完成させました。今回の記事から、過去の手番に戻ることができるタイムトラベル機能を実装していきます。今回はその準備として、過去の盤面の状況を記憶するために、新たにコンポーネントを設計していきます。

タイムトラベル(1)

次のページで、前回のソースファイルを確認できます。

  • 前回の内容はコチラから!

過去の手番を保持するアプローチ

現在、マルバツゲームは Board コンポーネントと Square コンポーネントで構成されています。

  • Board コンポーネント
    • 盤面を表示する
    • state 型の squares 配列で盤面の状況を管理する
    • Square コンポーネントがクリックされたときのイベントとして handleClick() 関数を定義している
  • Square コンポーネント
    • 盤面上にある1つのマスを表示する

そこで、手番毎の盤面の状況を保持する Game コンポーネントを作成していきます。このコンポーネントに、Board コンポーネントの state 型の変数と配列をリフトアップします。

コーディング

App.js にある Board コンポーネントを次のように変更します。

  • メインコンポーネントとしての定義をやめる
  • state 型の変数と配列の宣言を省く
  • 次のプロパティを設定する
    • xIsNext : 現在の手番
    • squares : 現在の盤面の状況
    • onPlay : 盤面の状況を記憶する処理と手番を交代する処理を行う関数
  • handleClick() 関数の変更
    • state 型の変数と配列の更新処理を省く
    • onPlay プロパティで受け取った関数を実行する
      • 更新された盤面の状況を渡す
App.js の Board コンポーネント
function Board({ xIsNext, squares, onPlay }) {
  const winner = calculateWinner(squares);
  let status;

  if (winner) {
    status = "勝者: " + winner;
  } else {
    status = "プレイヤー: " + (xIsNext ? "X" : "O");
  }

  function handleClick(i) {
    if (squares[i]) {
      return;
    }

    const nextSquares = squares.slice();

    if (xIsNext) {
      nextSquares[i] = "X";
    } else {
      nextSquares[i] = "O";
    }
    onPlay(nextSquares);
  }

  return (
    <>
      <div>{status}</div>
      <div className="board-row">
        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />
        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />
        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />
        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />
        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />
        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />
        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />
      </div>
    </>
  );
}

続いて、 Game コンポーネントを次のように定義します。

  • これをメインコンポーネントとする
  • Board コンポーネントのリフトアップする
    • state 型の history 配列を定義する
      • 盤面の状況を示す
      • 初期値: [Array(9).fill(null)]
      • set関数名: setHistory
    • state 型の xIsNext 変数を定義する
      • 現在の着手を示す
      • 初期値: true
      • set関数名: setXIsNext
    • currentSquares 変数を定義する
      • 現在の盤面の状況を示す
      • 初期値: 最新の盤面の状況
  • handlePlay() 関数の定義
    • 盤面の状況を記憶する
    • 手番を交代する
  • Board コンポーネントをレンダーする
    • 各プロパティに適切な値または関数を渡す
App.js の Game コンポーネント
export default function Game() {
  const [history, setHistory] = useState([Array(9).fill(null)]);
  const [xIsNext, setXIsNext] = useState(true);
  const currentSquares = history[history.length - 1];

  function handlePlay(nextSquares) {
    setHistory([...history, nextSquares]);
    setXIsNext(!xIsNext);
  }

  return (
    <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />
  );
}

次に実行結果を示します。リフトアップをしたためコンポーネントの構成が変わりましたが、動作内容は前回と同じになります。

動作結果.gif

これで、盤面の状況は Game コンポーネントにある state 型の history 配列に記録できるようになりました。history 配列の要素は、ある手番における盤面の状況を示します。具体的には、history[i] という要素は i 回目の手番における盤面の状況、history[i][0] は盤面の左上にあるマスの状態を示します。

おわりに

今回は、タイムトラベルを実装するにあたり Game コンポーネントを定義したリフトアップをしていきました。次のページに現段階のソースファイルを示します。

次回は、Gameコンポーネントにテキストを過去の手番に戻れるようにしていきます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0