[前回] Django+Reactで学ぶプログラミング基礎(25): Reactチュートリアル(タイムトラベル機能)
はじめに
前回は、タイムトラベル機能を追加しました。
今回は、その続きです。
今回の内容
- タイムトラベル機能を追加
- 過去の着手を表示
- Keyを選ぶ
過去の着手を表示
React要素は第一級JavaScriptオブジェクトで、アプリ内で受け渡し可能
-
第一級オブジェクトの性質
- 変数やデータ構造に格納できる
- 関数のパラメータ(引数)として渡せる
- 関数の戻り値として返却可能
- プロパティやメソッドを持つことができる
- 無名関数(無名リテラル)として表現できる
- 実行時に動的生成できる
-
Reactで複数要素を描画するには、React要素の配列を使う
- JavaScript配列の
map()
メソッドを用いて、データを別のデータにマッピング可能
- JavaScript配列の
const numbers = [1, 2, 3];
const doubled = numbers.map(x => x * 2); // [2, 4, 6]
着手履歴の配列をマッピングし、画面上のボタンを表すReact要素を作成
- 過去の手番に
ジャンプ
するためのボタン一覧を表示 -
Game
のrender
メソッド内でhistory
にmap
を適用-
history
配列をループ処理する部分で-
step
変数は、history
内の現在の要素を表す -
move
変数は、現在要素のインデックスを表す
-
-
src/index.js
render() {
const history = this.state.history;
const current = history[history.length - 1];
const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
<li>
<button onClick={() => this.jumpTo(move)}>{desc}</button>
</li>
);
});
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
}
return (
<div className="game">
<div className="game-board">
<Board
squares={current.squares}
onClick={(i) => this.handleClick(i)}
/>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
</div>
</div>
);
}
着手履歴に該当するボタン<button>
を持つリストアイテム<li>
を作成
-
ボタンには
onClick
ハンドラが存在-
this.jumpTo()
というメソッドを呼び出す(実装は後で) - これにより、ゲーム内で行われた着手のリストが表示される
-
key
を選ぶ
-
ブラウザで、デベロッパーツール(F12で起動)のコンソールに出力される警告に着目
-
key
は配列またはイテレータの特別なプロパティで、Reactによって予約される- Reactが、どの要素が変更/追加/削除されたかを識別する際、自動的に使用される
- 配列内の項目を正しく識別するため、それぞれの項目に
key
を与えるべき -
key
をthis.props.key
のような形では、参照できない - コンポーネントが自身の
key
を確認する方法はない
-
動的リストを構築する場合、正しい
key
を割り当てることが強く推奨される- 適切な
key
がない場合、データ構造を再構成し、key
を付与すべき
- 適切な
-
key
が指定されなかった場合、Reactは警告を表示する- デフォルトの
key
として、配列のインデックスが使用される - 配列のインデックスを
key
として用いることは、トラブルの元- 項目の並び替え/挿入/削除などの操作で問題が発生する
- 明示的に
key={i}
を渡すことで、警告を消すことはできるが- 配列のインデックスを使う場合と同様、問題が生じるため推奨されない
- デフォルトの
-
key
はグローバルに一意である必要はない- コンポーネントとその兄弟間で一意であれば十分
-
リストをレンダリングする際、各リスト項目に
key
情報を持たせるべき- リストが変更された場合、Reactがどのアイテムが変更されたか把握できる
- リストアイテムに対する、追加/削除/並び替え/中身書き換え、などの操作で
- リストの各項目に
key
プロパティを与えることで、兄弟要素と区別できる
- リストの各項目に
<li key={user.id}>{user.name}: {user.taskCount} tasks left</li>
-
key
を用いた、リストの再レンダリング- Reactは新リスト項目の
key
に対し、旧リスト項目内に同じkey
を持つものが存在しないか探す- 旧リストに存在しなかった
key
が、新リストに含まれている場合- Reactはコンポーネントを新規作成
- 旧リストに存在したいずれの
key
も、新リストに含まれていない場合- Reactは以前のコンポーネントを破棄
- 2つの
key
がマッチした場合- 該当コンポーネントは移動される
- 旧リストに存在しなかった
- Reactは新リスト項目の
-
key
は、Reactにコンポーネントの同一性に関する情報を与える- よって、Reactが再レンダリングされても、
state
を保持できる - コンポーネントの
key
が変わった場合- コンポーネントは破棄され、新しい
state
で再作成される
- コンポーネントは破棄され、新しい
- よって、Reactが再レンダリングされても、
おわりに
着手履歴の画面表示とKey
プロパティを勉強しました。
次回も続きます。お楽しみに。