[前回] Django+Reactで学ぶプログラミング基礎(26): Reactチュートリアル(着手履歴表示とKeyプロパティ)
はじめに
前回は、タイムトラベル機能に必要な知識を勉強しました。
今回は、その実装を完成させ、チュートリアルのまとめを行います。
今回の内容
- タイムトラベル機能を追加
- タイムトラベルを実装
- まとめ
タイムトラベルを実装
三目並べゲームの着手履歴を表すリスト項目のkey
を決める
- すべての着手には、関連付けられた一意なIDが存在
- 着手順のインデックスを表す連番数字
- ゲーム中に、着手が
削除/挿入/並び替え
されることはない- よって、着手のインデックスを
key
として使うのは安全
- よって、着手のインデックスを
-
Game
コンポーネントのrender
メソッド内で、key
を追加<li key={move}>
src/index.js
const moves = history.map((step, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
<li key={move}>
<button onClick={() => this.jumpTo(move)}>{desc}</button>
</li>
);
});
リスト項目内のボタンクリックによりコールされるjumpTo
メソッドを実装
- まず、
Game
コンポーネントのstate
に、いま何手目の状態を見ているかを表すstepNumber
プロパティを追加-
Game
のconstructor
内で、state
の初期値としてstepNumber: 0
を追加
-
src/index.js
class Game extends React.Component {
constructor(props) {
super(props);
this.state = {
history: [{
squares: Array(9).fill(null),
}],
stepNumber: 0,
xIsNext: true,
};
}
- 次に、
Game
内にjumpTo
メソッドを定義-
stepNumber
を更新する -
stepNumber
の値が偶数の場合、xIsNext
をtrue
に設定- 偶数かの判断に、厳密等価演算子(
===
)を使用- オペランド比較時、型変換(キャスト)しない
- 二つのオペランドの型が異なる場合、常に異なるものと判断
- 偶数かの判断に、厳密等価演算子(
- ※
setState
でstate
のhistory
プロパティは指定しなくてよい(変更不要のため)- 理由は、
state
の更新はマージされるため- Reactは
setState
で指定されたプロパティは更新、未指定のプロパティはstate
にそのまま残す
- Reactは
- 理由は、
-
src/index.js
jumpTo(step) {
this.setState({
stepNumber: step,
xIsNext: (step % 2) === 0,
});
}
マス目をクリックしたときに実行されるGame
コンポーネントのhandleClick
メソッドを変更
-
state
のstepNumber
プロパティは、現在ユーザに見せている着手を表す- 新しい着手が発生した場合、
this.setState
の引数にstepNumber: history.length
を加え、stepNumber
を更新
- 新しい着手が発生した場合、
-
時間を巻き戻し
過去の着手に戻った場合、それより新しい着手履歴を捨て去る-
this.state.history
を読み取る処理を、this.state.history.slice(0, this.state.stepNumber + 1)
に書き換える
-
src/index.js
handleClick(i) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
const squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? 'X' : 'O';
this.setState({
history: history.concat([{
squares: squares
}]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext,
});
}
- 最後に、常に最後の着手後状態をレンダリングするのではなく、
stepNumber
によって現在選択されている着手をレンダリング- ゲーム履歴内のいずれの手番をクリックしても、盤面は該当着手が発生した直後の状態を表示するようになる
-
Game
コンポーネントのrender
を書き換える
src/index.js
render() {
const history = this.state.history;
const current = history[this.state.stepNumber];
const winner = calculateWinner(current.squares);
まとめ
おめでとうございます! 三目並べゲーム完成です。
チュートリアルで学んだ概念
- React要素
- Reactコンポーネント
Props
State
-
State
VS.Props
- 共通点
- Reactコンポーネントの以下操作で使用される
- データの受け渡し
- ハンドリング
- 画面表示
- Reactコンポーネントの以下操作で使用される
- 違い
-
State
: コンポーネントが持っている状態 -
Props
: 親コンポーネントから渡されたプロパティ
-
- 使い分け
- コンポーネント内部状態の操作には
State
を使用 - 直接外部コンポーネントの
State
に接続する場合は、Props
で受け渡す
- コンポーネント内部状態の操作には
- 共通点
ゲーム機能のまとめ
- 三目並べが遊べる
- 決着がついたときに表示できる
- ゲーム進行にあわせ履歴が保存される
- 着手履歴の見直しや盤面の過去状態を参照できる
改良アイディアにチャレンジ
- 履歴内の着手の位置を
(col, row)
というフォーマットで表示 - 着手履歴のリスト中で現在選択されているアイテムを太字にする
-
Board
でマス目を並べる部分を、ハードコーディングでなく、2つのループを使用するように書き換える - 着手履歴のリストを昇順/降順いずれでも並べかえられるよう、トグルボタン(toggle button)を追加
- どちらかが勝利した際に、勝利につながった3つのマス目をハイライト
- どちらも勝利しなかった場合、引き分けメッセージを表示
おわりに
チュートリアルを通じて、Reactの基本を勉強しました。
次回も続きます。お楽しみに。