はじめに
いまさらですがReact(react.js)をはじめてみました。勉強会の宿題で、サイコロのアプリを作ってこい、とのことなので、勉強も兼ねてやってみました。
本当に基礎の基礎的なことは省いているので、ものすごく丁寧には解説してません。
できたのが、これです。←クリック!
用意
ローカルPC上に、create-react-app
してもいいのですが、サクッと作りたかったので、今回はブラウザ上で手軽にReact環境が作れるエディタのcodesandbox上に作りました。


手順
index.jsにあるコードを一旦、全部消して、必要なものだけ書いていきます。
まず、ES2015のシンタックスを使ってJSのモジュールを読み込みます。
import React from "react";
React
という名前で"react"
というモジュールから読み込みを行う、と言う意味。こうすることで、このファイルの中で定義してない関数や変数やクラスとかが使えるようになる(render
とかが使えるようになります)。
もうひとつ読み込みます。"react-dom"
というパッケージからrender
を読み込みます。
import { render } from "react-dom";
そして、上記で読み込んだrender
を早速render関数(メソッド)を使ってみます。シンタックスは以下の通り
render(JSX, target DOM Element)
実際にはこう書きました。
render(<DiceApp />, document.getElementById("root"));
こう書くことで、HTML上にある#root
のDOMに、Reactで作ったコンポーネントをレンダリングできます。
現状こんな感じです。
import React from "react";
import { render } from "react-dom";
render(<DiceApp />, document.getElementById("root"));
でも、このままだと、DiceAppが定義されておらずReferenceErrorになります。
第一引数であるReactComponent
の<DiceApp />
の説明は後述します。
あ、ちなみに、この第一引数には以下のような要素などが入ります。
-
ReactElement
←JSのオブジェクト- クラスの実体といってもいい
-
ReactComponent
←JSのクラス(コンストラクタ関数)- 何らかの入力に応じてオブジェクト(実体)ができます
ちなみに...ソースコード→マシン語
変換のコンパイルではなく、ソースコード(JSX)からソースコード(JSのObject)への変換だからトランスコンパイルなのですね。
参考:http://js.studio-kingdom.com/react/glossary/#react_components
ReactComponent を作る
まず、renderメソッドの第一引数にReactComponentを入れる場合、最初の文字は大文字、< />
で囲うというルールがあります。
次に、stateを持ったReactのクラスで書くコンポーネント(ReactComponent)を理解するには、ES2015のクラスシンタックスをまず理解したほうがいいので、説明します。
ES2015のクラスシンタックス
犬オブジェクトを生成するクラスを宣言しました。
class Dog {
constructor() {
}
}
これが最小限のクラスの書き方です。
また、コンストラクタ(constructor()
)は、classによって定義されるオブジェクトの生成時に、初期化を行う特別なメソッドです。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes
別で、オブジェクトを生成する際、コンストラクタで引数を1個なり2個なり受け取って、生成されます。
Reactのstateを持ったクラスで書くReactComponent
基本は同じですが、ES2015のクラスシンタックスと、少し違います。
今回も、犬オブジェクトを生成するクラスを宣言しました。
class Dog extends React.Component{
constructor(props) {
super(props)
}
render() {
return <h1>ワン🐶</h1>;
}
}
extends React.Component
は、ReactComponentの性質や状態を持ったものを拡張して(extends
)使う、という意味です。こうすることで、DogクラスはReactComponentの性質を持った状態になれます。
次に、コンストラクタの中身です。
constructorで(props)を受け取って、それをsuper(props)で実行すると言う意味です。
次に、render()。これは必ず持たないといけないそうです。
DiceAppのReactComponentをつくります
ここまでの話を受けて、DiceAppのReactComponentを作ります。
import React from "react";
import { render } from "react-dom";
class DiceApp extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h1>DiceApp</h1>;
}
}
render(<DiceApp />, document.getElementById("root"));
これでエラーは出なくなりました。
stateをもたせる
そして、サイコロの目の部分は1〜6で変化していくところなので、その部分にstateを持たせていきます。コンストラクタの中で、this.state
で変数として宣言します。変数にはオブジェクトで渡さなきゃなので{}
というシンタックスで渡します。
import React from "react";
import { render } from "react-dom";
class DiceApp extends React.Component {
constructor(props) {
super(props);
this.state = { num: 1 };
}
render() {
return (
<div>
<h1>DiceApp</h1>
<p>サイコロの目:{this.state.num}</p>
</div>
);
}
}
render(<DiceApp />, document.getElementById("root"));
ボタンを押したら、サイコロの目が変わるようにします
<p>サイコロの目:{this.state.num}</p>
の下に、<button>サイコロをふる</button>
をつけましょう。
ボタンをクリックしたら自分自身のメソッドを呼び出すようにします(コールバック)。
import React from "react";
import { render } from "react-dom";
class DiceApp extends React.Component {
constructor(props) {
super(props);
this.state = { num: 1 };
}
render() {
return (
<div>
<h1>DiceApp</h1>
<p>サイコロの目:{this.state.num}</p>
<button onClick={this.shakeDice}>サイコロをふる</button>
</div>
);
}
shakeDice = () => {};
}
render(<DiceApp />, document.getElementById("root"));
なにも起こりませんが、一応メソッドも定義しました。アロー関数なのは、JSX部分で使われているメソッド(コールバックしてる)部分{onClick={this.shakeDice}}
のthis
の先がshakeDice
メソッドと明示させるためです。他にもいろんな方法はありますが、create-react-app
の場合はこれが一番らしいです。
クリックされた際に自分自身のstateを変える際、setState
を使います。
import React from "react";
import { render } from "react-dom";
class DiceApp extends React.Component {
constructor(props) {
super(props);
this.state = { num: 1 };
}
render() {
return (
<div>
<h1>DiceApp</h1>
<p>サイコロの目:{this.state.num}</p>
<button onClick={this.shakeDice}>サイコロをふる</button>
</div>
);
}
shakeDice = () => {
this.setState({ num: 2 });
};
}
render(<DiceApp />, document.getElementById("root"));
そうすると、ボタンをクリックした際に、1から2に変わります。
ここまでくれば、あとはJSだけの知識でできます。
Math.random()
に6をかけて乱数の「範囲」を指定して、小数点が出ないようにfloor()
メソッドを使って、0が出ないように+1してちょっと調節すれば完了です。
import React from "react";
import { render } from "react-dom";
class DiceApp extends React.Component {
constructor(props) {
super(props);
this.state = { num: 1 };
}
render() {
return (
<div>
<h1>DiceApp</h1>
<p>サイコロの目:{this.state.num}</p>
<button onClick={this.shakeDice}>サイコロをふる</button>
</div>
);
}
shakeDice = () => {
this.setState({ num: Math.floor(Math.random() * 6) + 1 });
};
}
render(<DiceApp />, document.getElementById("root"));
コンポーネント部分のファイルを分ける
このままでも機能はするのですが、学びのために、importとexportという、他のファイルから読み込むES2015のシンタックスを利用して、コンポーネント部分と、それを呼び出す部分を分けましょう。
ファイル構成はこんな感じにしましょう。
まず、新たに作成したdice-component.js
を見ていきましょう。
import React from "react";
export class DiceApp extends React.Component {
constructor(props) {
super(props);
this.state = { num: 1 };
}
render() {
return (
<div>
<h1>DiceApp</h1>
<p>サイコロの目:{this.state.num}</p>
<button onClick={this.shakeDice}>サイコロをふる</button>
</div>
);
}
shakeDice = () => {
this.setState({ num: Math.floor(Math.random() * 6) + 1 });
};
}
変わったことといえば
- 別ファイルでもちゃんと
import React from "react";
をしている- (Reactのコンポーネントを作っているので)
-
DiceApp
のReactComponentをexport
している
くらいですね。続いて、index.js
はだいぶスッキリしましたね。
import React from "react";
import { render } from "react-dom";
import { DiceApp } from "./dice-component";
render(<DiceApp />, document.getElementById("root"));
import { DiceApp } from "./dice-component";
で呼び出した分、だいぶ見た目がスッキリしました。
このように、コンポーネント単位でファイルを分けると、いくつもコンポーネントを作る際は見通しがよくなると思います。
以上です!