React について、開発を行うために必要な基本事項について記載します。
JSX 構文で React element を定義
ReactDOM.render(element, container) の第一引数に指定する React element は JSX で記述できます。
const element = <h1>Hello, world!</h1>;
React element は関数から返すことができます。タグは入れ子にすることができます。{}
で値を埋め込むこともできます。その場合、属性値はダブルクォーテーション等で囲わないようにします。
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
function getGreeting(user) {
if (user) {
return <h1 id={user.id}>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
const user = {
firstName: 'Harper',
lastName: 'Perez',
id: 'myid'
};
const element = (
<div>
{getGreeting(user)}
<h2>Good to see you here.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
以下のような仮想 DOM が生成されます。
<div id="root">
<div>
<h1 id="myid">Hello, Harper Perez!</h1>
<h2>Good to see you here.</h2>
</div>
</div>
const
で定義していることからも分かるとおり React element はイミュータブルです。再描画する場合は新規 React element を構築して ReactDOM.render()
に再度渡します。ReactDOM.render()
は変更のあった仮想 DOM だけを更新します。このことを、以下のコードでは setInterval を利用して確認しています。
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
React component から React element を返す
React element を返すものは React component とよばれます。React component は function として記述することもできますが、実際には後述の state などが利用できる class として記述されます。
function として定義された React component
function App(props) {
return <h1>Hello, {props.name}</h1>;
}
ReactDOM.render(
<App name="myname" />,
document.getElementById('root')
);
class として定義された React component
class App extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
ReactDOM.render(
<App name="myname" />,
document.getElementById('root')
);
慣習として、アプリケーション全体を React で記述するときは App
という名称のコンポーネントを root に render します。Rails テンプレートエンジン内で利用する場合などは複数箇所で render するためその限りではありません。
状態をもつ React component
class で記述した React component は、状態 state
や componentDidMount
, componentWillUnmount
といったライフサイクルフックを持つことができます。
class Clock extends React.Component {
constructor(props) {
super(props);
// コンストラクタ内では setState を
// 利用せず state に直接代入します。
this.state = {
date: new Date(),
cnt: 0
};
}
// render 後に実行されるライフサイクルフックです。
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
// 以下のようにしても同じ意味です。
// const self = this;
// this.timerID = setInterval(
// function(){ self.tick() },
// 1000
// );
}
// 仮想 DOM から本 component が削除される前に実行される
// ライフサイクルフックです。
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
// コンストラクタ外では setState を利用します。
this.setState({
date: new Date()
});
// setState は上記のオブジェクトを引数に指定するものと
// 下記の関数を引数に指定するものがあります。
this.setState((prevState, props) => {
return {
cnt: prevState.cnt + parseInt(props.incr)
};
});
// 以下のようにしても同じ結果です。
// this.setState(prevState => {
// return {
// cnt: prevState.cnt + 5
// };
// });
// 以下のようにしても同じ意味です。
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
// this.setState((prevState, props) => ({
// cnt: prevState.cnt + parseInt(props.incr)
// }));
// this.setState(function(prevState, props){
// return {
// cnt: prevState.cnt + parseInt(props.incr)
// };
// });
}
// React element を返します。
render() {
return (
<div>
<p>time: {this.state.date.toLocaleTimeString()}</p>
<p>cnt: {this.state.cnt}</p>
</div>
);
}
}
ReactDOM.render(
<Clock incr="5" />,
document.getElementById('root')
);
イベントハンドリング
React におけるイベントハンドラは以下のように記述します。SyntheticEvent に記載のあるイベントをハンドリングできます。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// `handleClick` 内で `this` を利用するために忘れずに記述します。
this.handleClick = this.handleClick.bind(this);
}
// `this` を利用するイベントハンドラ
handleClick(e) {
e.preventDefault(); // <a> タグの画面遷移を無効化したい場合
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
// 引数のあるイベントハンドラ
handleClickWithArgs(myarg, e) {
console.log(myarg);
}
render() {
return (
<div>
<a href="#" onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</a>
<button onMouseOver={this.handleClickWithArgs.bind(this, 123)}>マウスオーバーでログ出力</button>
</div>
);
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('root')
);
条件分岐
if 文による分岐以外に、以下のような記法が利用できます。
ReactDOM.render(
<div>
<h1>hello</h1>
{true && <p>表示される</p>}
{false && <p>表示されない</p>}
{true ? <p>表示される</p> : <p>表示されない</p>}
</div>,
document.getElementById('root')
);
React component が React element として null
を返すと何も表示されません。
function App(props) {
if(!props.show) {
return null;
}
return <h1>hello</h1>;
}
ReactDOM.render(
<App show={false} />,
document.getElementById('root')
);
複数の React elements を返す React component
React component は複数の React elements を返すことができます。map を利用すると便利です。
function ListItem(props) {
// `map` 内ではないため `key` を指定する必要はありません。
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(number =>
<ListItem
key={number.toString()} // `map` 内で生成する要素には何らかの `key` を指定します。
value={number} /> // `key` は props として渡されません。必要に応じて別途指定します。
);
const listItems2 = numbers.map((number, index) => // `index` も利用できます (非推奨)
<ListItem
key={index} // `key` は同じリスト `listItems2` 内でユニークであれば十分です。
value={number} />
);
return (
<div>
<ul>{listItems}</ul>
<ul>{listItems2}</ul>
</div>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
React component の階層化
様々な component の共通部分を汎用的な component として切り出して、それを複数 component の内部で利用することでコード全体のメンテナンス性が上がります。このような階層化された component 群に関するいくつかの有益なサンプルを示します。
複数 component で状態を共有
上位 component の state を下位 component に渡すことで、component 群が全体として持つ状態を統一できます。上位 component は下位 component にコールバック関数を合わせて渡しておき、下位 component 内で状態を更新する必要が発生した場合はそれを呼び出して上位 component の state を更新します。
class MyInnerComponent extends React.Component {
constructor(props) {
super(props);
// 前述のとおり `handleChange` 内で `this` を利用するために忘れずに記述します。
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onMyChange(e.target.value); // input の値を取得してコールバック関数を実行します。
}
render() {
return <input value={this.props.myval} onChange={this.handleChange} />;
}
}
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.handleChange1 = this.handleChange1.bind(this); // 前述のとおり `handleChange1/2` 内で
this.handleChange2 = this.handleChange2.bind(this); // `this` を利用するために忘れずに記述します。
this.state = {myval: 0.0};
}
handleChange1(v) {
const myval = parseFloat(v); // そのまま格納
this.setState({myval: Number.isNaN(myval) ? 0.0 : (myval / 1.0)});
}
handleChange2(v) {
const myval = parseFloat(v); // 半分にして格納
this.setState({myval: Number.isNaN(myval) ? 0.0 : (myval / 2.0)});
}
render() {
const myval = this.state.myval; // そのまま表示、二倍にして表示
return (
<div>
<MyInnerComponent myval={myval * 1.0} onMyChange={this.handleChange1} />
<MyInnerComponent myval={myval * 2.0} onMyChange={this.handleChange2} />
</div>
);
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('root')
);
React elements を props として渡す
props.children
を利用すると以下のような React element の渡し方ができます。
function MyInnerComponent(props) {
return (
<div>
{props.children}
</div>
);
}
function MyComponent() {
return (
<MyInnerComponent>
<h1>hello</h1>
</MyInnerComponent>
);
}
ReactDOM.render(
<MyComponent />,
document.getElementById('root')
);
複数の React element を渡す必要がある場合は、通常どおり属性値として渡します。
function MyInnerComponent(props) {
return (
<p>
{props.p1}, {props.p2}
</p>
);
}
function MyComponent() {
return (
<MyInnerComponent
p1={
<span>Hello</span>
}
p2={
<span>world!</span>
}
/>
);
}
ReactDOM.render(
<MyComponent />,
document.getElementById('root')
);
form の制御
React を使用しない場合であっても、一般に form タグ内の input 要素は状態を持ちます。これを React state と一致させることで form の状態を React から制御できます。
input タグ、textarea タグ、select タグ
以下の例において、入力文字列が変化すると、大文字化されて state に格納されて、即時で input 要素の value が更新されます。結果として、小文字が入力できない form を実現できます。注意すべき箇所は以下のとおりです。
- JSX において
<textarea>...</textarea>
は<textarea value={...} />
と記載される - JSX において
<option selected ...
は<select value={} ...
と記載される
input/textarea/select すべてを類似の記法で扱えることが分かります。
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''}; // input の初期値です。
// `handleChange/Submit` 内で `this` を利用するために忘れずに記述します。
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()}); // 大文字化してみます。
}
handleSubmit(event) {
alert('submitted value: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input value={this.state.value} onChange={this.handleChange} />
<textarea value={this.state.value} onChange={this.handleChange} />
<select value={this.state.value} onChange={this.handleChange}>
<option value="AAA">AAA</option>
<option value="BBB">BBB</option>
</select>
<input type="submit" />
</form>
);
}
}
ReactDOM.render(
<MyForm />,
document.getElementById('root')
);
チェックボックス、ラジオボタン
input 要素毎に異なる state を持たせる場合でイベントハンドラを共用にする場合、event.target
の type
や name
で更新すべき state を識別します。以下の例ではチェックボックスとラジオボタンがそれぞれ別々の state を持っており、どちらも handleChange をイベントハンドラとして利用しています。
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
checkboxValue: true,
radioValue: 'aaa'
};
// `handleChange` 内で `this` を利用するために忘れずに記述します。
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
console.log(this.state);
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names
this.setState({
[name]: value
});
}
render() {
return (
<form>
<input
name="checkboxValue"
type="checkbox"
checked={this.state.checkboxValue}
onChange={this.handleChange} />
<input
name="radioValue"
type="radio"
value="aaa"
checked={this.state.radioValue === 'aaa'}
onChange={this.handleChange} />
<input
name="radioValue"
type="radio"
value="bbb"
checked={this.state.radioValue === 'bbb'}
onChange={this.handleChange} />
</form>
);
}
}
ReactDOM.render(
<MyForm />,
document.getElementById('root')
);
よく利用する npm パッケージ
記事の執筆者にステッカーを贈る
有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。
さらに詳しく →Feedbacks
ログインするとコメントを投稿できます。
関連記事
- JavaScriptにおける関数定義function文で定義します。 sample.js function fact(n) { return (n==0) ? 1 : n*fact(n-1); } for(var i=0; i<7; ++i) console.log(i +'!='+ fact(i)); 実行例 $ node sample.js 0!=1 1!=1 2!=2 3!=6 4!=24 5!=120 6!=...
- Date オブジェクトの使用例 (JavaScript)コンストラクタ // 現在時刻 var d = new Date(); console.log(d); // システムのタイムゾーンに変換して出力 (この例では JST) //=> Thu Jan 29 2015 23:45:21 GMT+0900 (東京 (標準時)) // システムのタイムゾーン (以下の例では JST) での日付指定 d = new Date('2000/01/01')...
- AngularJS が提供するユーティリティAngularJS では JavaScript の言語仕様として不足のある汎用的な処理を補うためにユーティリティが提供されています。代表的なものをコード例とともに列挙します。公式ドキュメントはこちらです。 <!DOCTYPE html> <html lang="ja" ng-app> <head> <meta charset="utf-8"> ...
- JavaScript のグラフ描画ツール Dygraph の使用例ダウンロード こちらのページから dygraph-combined.js をダウンロードします。 サンプルコード チュートリアルより。 <html> <head> <script type="text
- サービス (AngularJS)AngularJS のビルトインサービスのうち代表的なものをコード例とともに列挙します。公式ドキュメントはこちらです。 <!DOCTYPE html> <html lang="ja" ng-app="myApp"> <head> <meta charset="utf-8"> <script src="angular.min.js">...