最終目標
フロントエンドとバックエンドのDB排他を調べてみたい。
今回の目標
Tutorial: Intro to Reactを実施する
https://reactjs.org/tutorial/tutorial.html
https://mae.chab.in/archives/2943
環境
Ubuntu 18.04.2 LTS
node v8.10.0
npm 3.5.2
create-react-app v2.1.5
詳細は、https://qiita.com/y_ohr/items/8afd0852b24356059f2b 参照。
Tutorial: Intro to React
ローカルでやります。
react-app作成
$ create-react-app tutorial-react-app
$ cd tutorial-react-app/
src配下削除
$ cd src/
$ rm -f *
$ cd ..
src/index.css作成
src/index.css
body {
font: 14px "Century Gothic", Futura, sans-serif;
margin: 20px;
}
ol, ul {
padding-left: 30px;
}
.board-row:after {
clear: both;
content: "";
display: table;
}
.status {
margin-bottom: 10px;
}
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 34px;
}
.square:focus {
outline: none;
}
.kbd-navigation .square:focus {
background: #ddd;
}
.game {
display: flex;
flex-direction: row;
}
.game-info {
margin-left: 20px;
}
src/index.js作成
src/index.js
class Square extends React.Component {
render() {
return (
<button className="square">
{/* TODO */}
</button>
);
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square />;
}
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</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>
);
}
}
class Game extends React.Component {
render() {
return (
<div className="game">
<div className="game-board">
<Board />
</div>
<div className="game-info">
<div>{/* status */}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
}
}
// ========================================
ReactDOM.render(
<Game />,
document.getElementById('root')
);
index.jsの先頭に3行追加。ここからはdiffにて。
src/index.js
diff --git a/src/index.js b/src/index.js
index 9a06c07..70f7c67 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,3 +1,7 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './index.css';
+
class Square extends React.Component {
render() {
return (
Passing Data Through Props
src/index.js
diff --git a/src/index.js b/src/index.js
index 70f7c67..43522c3 100644
--- a/src/index.js
+++ b/src/index.js
@@ -6,7 +6,7 @@ class Square extends React.Component {
render() {
return (
<button className="square">
- {/* TODO */}
+ {this.props.value}
</button>
);
}
@@ -14,7 +14,7 @@ class Square extends React.Component {
class Board extends React.Component {
renderSquare(i) {
- return <Square />;
+ return <Square value={i} />;
}
render() {
Making an Interactive Component
src/index.js
diff --git a/src/index.js b/src/index.js
index 43522c3..b908669 100644
--- a/src/index.js
+++ b/src/index.js
@@ -3,10 +3,19 @@ import ReactDOM from 'react-dom';
import './index.css';
class Square extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ value: null,
+ };
+ }
+
render() {
return (
- <button className="square">
- {this.props.value}
+ <button
+ className="square"
+ onClick={() => this.setState({ value: 'X' })}>
+ {this.state.value}
</button>
);
}
Lifting State Up
src/index.js
diff --git a/src/index.js b/src/index.js
index b908669..01c54da 100644
--- a/src/index.js
+++ b/src/index.js
@@ -3,27 +3,38 @@ import ReactDOM from 'react-dom';
import './index.css';
class Square extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- value: null,
- };
- }
-
render() {
return (
<button
className="square"
- onClick={() => this.setState({ value: 'X' })}>
- {this.state.value}
+ onClick={() => this.props.onClick()}>
+ {this.props.value}
</button>
);
}
}
class Board extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ squares: Array(9).fill(null),
+ };
+ }
+
+ handleClick(i) {
+ const squares = this.state.squares.slice();
+ squares[i] = 'X';
+ this.setState({ squares: squares });
+ }
+
renderSquare(i) {
- return <Square value={i} />;
+ return (
+ <Square
+ value={this.state.squares[i]}
+ onClick={() => this.handleClick(i)}
+ />
+ );
}
render() {
Function Components
src/index.js
diff --git a/src/index.js b/src/index.js
index 01c54da..5f16f3d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -2,16 +2,14 @@ import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
-class Square extends React.Component {
- render() {
- return (
- <button
- className="square"
- onClick={() => this.props.onClick()}>
- {this.props.value}
- </button>
- );
- }
+function Square(props) {
+ return (
+ <button
+ className="square"
+ onClick={props.onClick}>
+ {props.value}
+ </button>
+ );
}
class Board extends React.Component {
Taking Turns
src/index.js
diff --git a/src/index.js b/src/index.js
index 5f16f3d..fa7e58a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -17,13 +17,17 @@ class Board extends React.Component {
super();
this.state = {
squares: Array(9).fill(null),
+ xIsNext: true,
};
}
handleClick(i) {
const squares = this.state.squares.slice();
- squares[i] = 'X';
- this.setState({ squares: squares });
+ squares[i] = this.state.xIsNext ? 'X' : 'O';
+ this.setState({
+ squares: squares,
+ xIsNext: !this.state.xIsNext,
+ });
}
renderSquare(i) {
@@ -36,7 +40,7 @@ class Board extends React.Component {
}
render() {
- const status = 'Next player: X';
+ const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
return (
<div>
Declaring a Winner
src/index.js
diff --git a/src/index.js b/src/index.js
index fa7e58a..34c275d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -23,6 +23,10 @@ class Board extends React.Component {
handleClick(i) {
const squares = this.state.squares.slice();
+ if(calculateWinner(squares) || squares[i]){
+ return;
+ }
+
squares[i] = this.state.xIsNext ? 'X' : 'O';
this.setState({
squares: squares,
@@ -40,7 +44,13 @@ class Board extends React.Component {
}
render() {
- const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
+ const winner = calculateWinner(this.state.squares);
+ let status;
+ if (winner) {
+ status = 'Winner: ' + winner;
+ } else {
+ status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
+ }
return (
<div>
@@ -87,3 +97,23 @@ ReactDOM.render(
<Game />,
document.getElementById('root')
);
+
+function calculateWinner(squares) {
+ const lines = [
+ [0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 8],
+ [0, 3, 6],
+ [1, 4, 7],
+ [2, 5, 8],
+ [0, 4, 8],
+ [2, 4, 6],
+ ];
+ for (let i = 0; i < lines.length; i++) {
+ const [a, b, c] = lines[i];
+ if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
+ return squares[a];
+ }
+ }
+ return null;
+}
Lifting State Up, Again
src/index.js
diff --git a/src/index.js b/src/index.js
index 34c275d..65a0010 100644
--- a/src/index.js
+++ b/src/index.js
@@ -13,48 +13,18 @@ function Square(props) {
}
class Board extends React.Component {
- constructor() {
- super();
- this.state = {
- squares: Array(9).fill(null),
- xIsNext: true,
- };
- }
-
- handleClick(i) {
- const squares = this.state.squares.slice();
- if(calculateWinner(squares) || squares[i]){
- return;
- }
-
- squares[i] = this.state.xIsNext ? 'X' : 'O';
- this.setState({
- squares: squares,
- xIsNext: !this.state.xIsNext,
- });
- }
-
renderSquare(i) {
return (
<Square
- value={this.state.squares[i]}
- onClick={() => this.handleClick(i)}
+ value={this.props.squares[i]}
+ onClick={() => this.props.onClick(i)}
/>
);
}
render() {
- const winner = calculateWinner(this.state.squares);
- let status;
- if (winner) {
- status = 'Winner: ' + winner;
- } else {
- status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
- }
-
return (
<div>
- <div className="status">{status}</div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
@@ -76,14 +46,56 @@ class Board extends React.Component {
}
class Game extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ history: [{
+ squares: Array(9).fill(null),
+ }],
+ xIsNext: true,
+ };
+ }
+
+ handleClick(i) {
+ const history = this.state.history;
+ 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,
+ }]),
+ xIsNext: !this.state.xIsNext,
+ });
+ }
+
render() {
+ const history = this.state.history;
+ const current = history[history.length - 1];
+ const winner = calculateWinner(current.squares);
+
+ 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 />
+ <Board
+ squares={current.squares}
+ onClick={(i) => this.handleClick(i)}
+ />
+
</div>
<div className="game-info">
- <div>{/* status */}</div>
+ <div>{status}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
Showing the Past Moves
src/index.js
diff --git a/src/index.js b/src/index.js
index 65a0010..5afcc7d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -78,6 +78,15 @@ class Game extends React.Component {
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;
@@ -96,7 +105,7 @@ class Game extends React.Component {
</div>
<div className="game-info">
<div>{status}</div>
- <ol>{/* TODO */}</ol>
+ <ol>{moves}</ol>
</div>
</div>
);
Implementing Time Travel
src/index.js
diff --git a/src/index.js b/src/index.js
index 5afcc7d..0412658 100644
--- a/src/index.js
+++ b/src/index.js
@@ -52,12 +52,13 @@ class Game extends React.Component {
history: [{
squares: Array(9).fill(null),
}],
+ stepNumber: 0,
xIsNext: true,
};
}
handleClick(i) {
- const history = this.state.history;
+ 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]) {
@@ -69,19 +70,27 @@ class Game extends React.Component {
history: history.concat([{
squares: squares,
}]),
+ stepNumber: history.length,
xIsNext: !this.state.xIsNext,
});
}
+ jumpTo(step) {
+ this.setState({
+ stepNumber: step,
+ xIsNext: (step % 2) === 0,
+ });
+ }
+
render() {
const history = this.state.history;
- const current = history[history.length - 1];
+ const current = history[this.state.stepNumber];
const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => {
const desc = move ? 'Go to move #' + move : 'Go to game start';
return (
- <li>
+ <li key={move}>
<button onClick={() => this.jumpTo(move)}>{desc}</button>
</li>
);
感想
- 写経
- 思ったより時間がかかった=充実したチュートリアルだった。
- inputで動的に仮想DOMが書き換わるようなプログラムを書いてみたい
以上