[前回] Django+Reactで学ぶプログラミング基礎(19): Reactチュートリアル(準備とReact概要)
はじめに
前回は、ReactコンポーネントとJSX構文を勉強しました。
今回も、Reactチュートリアルが続きます。
今回の内容
- スターターコードを作成
- データを
Props
経由で渡す - インタラクティブなコンポーネントを作る
スターター(starter)コードを作成
-
ゲームアプリのベースとなるスターターコードを作成
- VS Codeで、
MY-APP
プロジェクトフォルダにあるsrc/index.js
を開く
- VS Codeで、
-
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;
}
- スターターコードに、3つのReactコンポーネントを追加
- Square(正方形のマス目)
- 1つの
<button>
をレンダリング
- 1つの
- Board(盤面)
- 9個のマス目をレンダリング
- Game
- 盤面とプレースホルダー(後述)を描画
- Square(正方形のマス目)
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>
);
}
}
// ========================================
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Game />);
- VS Codeコマンドプロンプトで、Webサーバーを起動(起動しなかった場合)
- 起動成功するまで待つ
C:\kanban\my-app>npm start
データをProps
経由で渡す
親であるBoard
コンポーネントから、子であるSquare
コンポーネントにprops
を渡す
-
Reactでは、親から子へ
props
を渡すことで- アプリ内で情報が流れていく
-
Board
のrenderSquare
メソッド内で、props
としてvalue
という名前の値をSquare
に渡す
src/index.js
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />;
}
}
-
Square
のrender
メソッドで、渡された値を表示する-
{/* TODO */}
を{this.props.value}
に書き換える
-
class Square extends React.Component {
render() {
return (
<button className="square">
{this.props.value}
</button>
);
}
}
インタラクティブなコンポーネントを作る
マス目がクリックされた場合、X
と表示
- まず、
Square
コンポーネントのrender()
関数から返されるbutton
タグを変更
class Square extends React.Component {
render() {
return (
<button className="square" onClick={function() { console.log('click'); }}>
{this.props.value}
</button>
);
}
}
アロー関数構文を用いて、イベントハンドラを記述
-
目的
- タイプ量を減らす
-
this
の混乱しやすい挙動を回避- クラス内で定義されたメソッドをクラス内で実行するためには、呼び出しの際に
this
をつける必要あり - イベントハンドラのコールバック関数が通常の関数の場合
-
this
はundefined
となる - 結果、エラーが起きる
-
- イベントハンドラのコールバック関数がアロー関数の場合
- アロー関数では、
this
が束縛されず - 正しく、コールバック関数外のスコープである、クラスコンポーネントのインスタンスを指してくれる
- アロー関数では、
- クラス内で定義されたメソッドをクラス内で実行するためには、呼び出しの際に
-
※ 注意
-
onClick={() => console.log('click')}
の記載で、onClick
プロパティに渡されるのは関数- Reactはクリックされるまでこの関数を実行しない
- よくある間違い:
onClick={console.log('click')}
-
src/index.js
class Square extends React.Component {
render() {
return (
<button className="square" onClick={() => console.log('click')}>
{this.props.value}
</button>
);
}
}
state
を用いて、コンポーネントの状態を記憶
-
Square
コンポーネントがクリックされたら-
state
を使用し、自分がクリックされたことを覚えさせる
- その後、
X
マークでマス目を埋める
-
-
Reactコンポーネントは、コンストラクタで
this.state
を設定することで、状態を持つことができる-
this.state
は、定義されているコンポーネント内でプライベートと見なされる - 現在の
Square
の状態をthis.state
に保存- マス目がクリックされた時に、
this.state
が変更される
- マス目がクリックされた時に、
-
コードを修正
- まず、クラスにコンストラクタを追加し、
state
を初期化-
constructor
を持つReactのクラスコンポーネントで、コンストラクタはsuper(props)
の呼び出しから始める- JavaScriptのクラスで、サブクラスのコンストラクタを定義時、常に
super
関数を呼び出す
- JavaScriptのクラスで、サブクラスのコンストラクタを定義時、常に
-
src/index.js
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null,
};
}
render() {
return (
<button className="square" onClick={() => console.log('click')}>
{this.props.value}
</button>
);
}
}
-
Square
のrender
メソッドを書き換え、クリックされた時にstate
の現在値を表示するように-
onClick={...}
というイベントハンドラをonClick={() => this.setState({value: 'X'})}
に書き換える-
onClick
ハンドラ内でthis.setState
を呼び出すことで-
<button>
がクリックされたら常に再レンダリングするように、Reactに伝える
-
-
setState
をコンポーネント内で呼び出すと、Reactはその内部の子コンポーネントも自動的に更新
-
-
<button>
タグ内のthis.props.value
をthis.state.value
に置き換える- マス目をクリックしたら、データ更新される
- この
Square
のthis.state.value
はX
になる - 盤面に
X
と表示される
- この
- マス目をクリックしたら、データ更新される
- 可読性のため、
className
とonClick
の両プロパティをそれぞれ独立した行に配置 - 修正後、
Square
のrender
メソッドから返される<button>
タグのコード- 修正ファイルの保存を忘れず(Ctrl+S)
-
src/index.js
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}
</button>
);
}
}
おわりに
Reactコンポーネント間のデータ受渡しと、
コンポーネント状態の保持方法を勉強しました。
次回も続きます。お楽しみに。