目次
- React のチュートリアルを Typescript でやってみた【環境構築編】
- React のチュートリアルを Typescript でやってみた【環境構築編2】
- React のチュートリアルを Typescript でやってみた【ソース準備編】
- React のチュートリアルを Typescript でやってみた1
- React のチュートリアルを Typescript でやってみた2
- React のチュートリアルを Typescript でやってみた3 ←ここ
概要
チュートリアル通りにやりますが、端折る箇所もあると思います。
各々保管をお願いいたします。
公式のチュートリアルページ
今回の実施チュートリアル内容
タイムトラベル機能の追加
- 着手の履歴の保存
- State のリフトアップ、再び
- 過去の着手の表示
- key を選ぶ
- タイムトラベルの実装
1.着手の履歴の保存
特にソースの変更なし
どこにhistoryを作成するべきか考える。
2.State のリフトアップ、再び
game.tsxに作成する必要があるためStateとPropsを作成。
historyに関しては、1つの要素に1ステージのデータが入っているとしたものを作成しました。
こうしないと後々エラーになりました(´・ω・`)
type stage = { squares: Array<String>; }
type State = {
history: Array<stage>;
xIsNext: boolean;
}
type Props = {}
その後コンストラクタを追加
constructor(props: Props) {
super(props);
this.state = {
history: [{
squares: Array(9).fill(null),
}],
xIsNext: true,
};
}
board.tsxの書き換え
game.tsxにStateを持つことになりこれまでデータを保持していたboard.tsxを大まかに変更する必要があります。
変更1:SteteからPropsに変更する。
Steteを削除し以下のPropsを作成
type Porps = {
squares: Array<String>;
onClick: (i: number) => void;
}
変更2:classの引数とコンストラクタの変更
class Board extends React.Component<Porps> {
constructor(props: Porps) {
super(props)
}
変更3:renderSquareの内容の変更
renderSquare(i: number) {
return <Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>;
}
変更4:handleClick及びcalculateWinnerのfunctionをgame.tsxに移動
大まかにはこのくらいまだまだ変更する必要がありますがとりあえずはって感じです。
game.tsxのrenderを書き換えていきます。
以下のような感じになりました。
render() {
const history = this.state.history;
const current = history[history.length - 1];
const winner = this.calculateWinner(current.squares);
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (this.state.xIsNext ? 'X' : '○');
}
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>{/* TODO */}</ol>
</div>
</div>
);
}
}
game.tsxを書き換えた事によってboard.tsxを書き換えます。
renderの部分のstatusについて削除しました。
render() {
return (
<div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
game.tsxに移動したhandleClickを書き換えます。
以下のような感じになりました。
ここのhistory: history.concat({ squares: squares }),
でかなり躓きました・・・
このようにするためにgame.tsxのStateがあのような形になった感じです。
もっといい方法があればオシエテクダサイ・・・
handleClick(i: number) {
const history = this.state.history;
const current = history[history.length - 1];
const squares = current.squares.slice();
if (this.calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? 'X' : '○';
this.setState({
history: history.concat({ squares: squares }),
xIsNext: !this.state.xIsNext,
});
}
以上でゲームができるような形になったと思います。
3.過去の着手の表示
game.tsxのrenderに以下のものを追加します。
render() {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[this.state.stepNumber];
const winner = this.calculateWinner(current.squares);
+ 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>
+ );
+ });
let status;
︙
<div className="game-info">
<div>{status}</div>
+ <ol>{moves}</ol>
</div>
︙
この時点ではWARNINGが表示されますが以降のものを実装することによってなくなるので放置します。
4.key を選ぶ
特にソースの変更なし
概念的には理解する必要あり
5.タイムトラベルの実装
movesの中身を変更します。
以下の変更でWARNINGが消えると思います。
return (
- <li>
+ <li key={move}>
<button /* onClick={() => this.jumpTo(move)} */>{desc}</button>
</li>
);
historyの機能のために
stepNumberを追加します。
type State = {
history: Array<stage>;
xIsNext: boolean;
+ stepNumber: number;
}
︙
constructor(props: Props) {
super(props);
this.state = {
history: [{
squares: Array(9).fill(null),
}],
xIsNext: true,
+ stepNumber: 0,
};
}
jumpToの実装
jumpTo(step: number) {
this.setState({
stepNumber: step,
xIsNext: (step % 2) === 0,
});
}
+ <button onClick={() => this.jumpTo(move)} >{desc}</button>
stepNumberの機能をhandleClickに反映させます。
チュートリアルのソースのままです。
handleClick(i: number) {
+ const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
const squares = current.squares.slice();
if (this.calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? 'X' : '○';
this.setState({
history: history.concat({ squares: squares }),
+ stepNumber: history.length,
xIsNext: !this.state.xIsNext,
});
}
最後にrenderする際の対象となるゲームのステージを設定します。
const history = this.state.history;
+ const current = history[this.state.stepNumber];
const winner = this.calculateWinner(current.squares);
まとめ
駆け足になりましたが、以上になります。
どのくらい吸収できたのかわかりませんがとりあえずチュートリアルは以上です。
何かしら自分で作ったほうがいいかもしれませんね。
ソースは以下