[前回] 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コンポーネント間のデータ受渡しと、
コンポーネント状態の保持方法を勉強しました。
次回も続きます。お楽しみに。








