React はユーザインターフェイスを構築するための、宣言型で効率的で柔軟な JavaScript ライブラリです。 複雑な UI を、「コンポーネント」と呼ばれる小さく独立した部品から組み立てることができます。
Reactはコンポーネントという単位で実装します。 Webサイトやアプリは、ボタンやタブなどのコンポーネントの集まりと考えることができます。 コンポーネントとは見た目と機能を組み合わせたものだと考えることができます。
Reactの世界では、Reactコンポーネントの記述方法が2つあります。 それはそれぞれ、関数コンポーネントとクラスコンポーネントと呼ばれます。
JSXはJavaScript内でHTMLのような表現をするJavascriptの拡張構文である。
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
{}をjsx内で用いることで変数や関数(returnあり)をjavascriptで埋め込むことが可能。
const fruit = 'りんご';
const price = 100;
const quantity = 5;
const element = <h1>{fruit}を{quantity}個買うと{price * quantity}円です</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
{}内でjavascriptにおけるコメントアウトを記述することで実現可能。 最後の例は改行しないと}もコメントアウトされてしまい、エラーとなるので改行はお忘れなく。
const user = 'admin';
const element = (
<div>
{/* コメント */}
{//コメント
user
}
</div> );
タグが空の場合、/>で閉じる
<div>
こんにちは
<br />
<img src="example.gif" />
</div>
JSXは親要素を1つしか持つことができません。 そのため親要素を1つにして子要素を2つにする方法がありますが、 <div><div>で子要素を囲うと不要なdivがDOMに追加されるため、フラグメント(React.Fragment)を用います。
const shrotFragment = (
<> {//内部のReact.Fragmentは省略
}
<div> hoge </div>
<div> fuga </div>
</>);{//内部のReact.Fragmentは省略
}
関数コンポーネントはJSXを返すプレーンなJavaScript関数である。
import React from "react";
const FunctionalComponent = () => {
return <h1>Hello, world</h1>;
};
クラスコンポーネントはReact.Componentを拡張するJavaScriptクラスです。 これにはrenderメソッドを含みます。
import React from "react";
class ClassComponent extends React.Component {
render() {
return <h1>Hello, world</h1>;
}
}
関数コンポーネントでは引数としてpropsを渡しています。ここでは非構造化を使用しています。
const FunctionalComponent = (props) => {
return <h1>Hello, {props.name}</h1>;
};
クラスコンポーネントではthisを使用してpropsを参照する必要があります。 この場合も、クラスベースのコンポーネントを使用しながら、非構造化を使用してprops内でnameを取得できます。
class ClassComponent extends React.Component {
render() {
const { name } = this.props;
return <h1>Hello, { name }</h1>;
}
}
Reactプロジェクトにおいてはstate変数の処理が不可欠です。 例として、0から始まるシンプルなカウンターを作成し、ボタンを1回クリックすると数が1ずつ増えるようにします。 useState()は、関数コンポーネントでstateを管理(stateの保持と更新)するためのReactフックであり、最も利用されるフックです。stateとはコンポーネントが内部で保持する「状態」のことで、画面上に表示されるデータ等、アプリケーションが保持している状態を指しています。
const FunctionalComponent = () => {
const [count, setCount] = React.useState(0);
return (
<div>
<p>count: {count}</p>
<button onClick={() => setCount(count + 1)}>Click</button>
</div>
);
};
関数コンポーネントでstate変数を使用するには、初期状態の引数を取るuseStateフックを使用する必要があります。 この場合は0クリックから開始するため、カウントの初期状態は0になります。
class ClassComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>count: {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click
</button>
</div>
);
}
クラスコンポーネントでのstateを処理する場合、概念は同じですが方法が少し異なります。 constructorの重要性を理解する必要があります。
基本的に、constructorを実装してsuper(props)の呼び出しを実行しないと、使用したいすべてのstate変数を定義できません。まずコンストラクタを定義しましょう。
ライフサイクルはレンダリングのタイミングにおいて重要な役割を果たします。 クラスコンポーネントはライフサイクルを持ちます。 ライフサイクルとは、コンポーネントが生まれて、成長し、死ぬまでの循環のことです。
コンポーネントが誕生した後は、度重なるrenderがユーザーの操作により発生します。 具体的にはrenderは、ユーザーの操作などによりstateが変更されるたびに呼び出されます。 これがライフサイクルの成長の過程であり、renderされる毎にcomponentDitUpdateという関数が実行されます。 コンポーネントが死ぬときつまりはアンマウントされる直前にcomponentWillUnmountという関数が実行されます。
クラスコンポーネントにおけるライフサイクルに関して説明します。
Mountingとは、コンポーネントが生まれるときの期間のことです。 マウントが行われる直前に実行されるcomponentWillMountとマウントが実装された直後に実行されるcomponentDidMountです。 componentWillMountは非推奨なので使わないほうがよいです。
Updatingとは、コンポーネントが変更される期間のことです。 つまり、コンポーネントが変更されるとはこのstateが変更されることを指します。 Reactにおける重要な点の一つは、このstateが変更されたときにrenderが走るということです。 つまり、stateが変更されるたびに画面の一部分を再描画していきます。
メソッド | 起動条件 |
---|---|
componentWillReactiveProps | コンポーネントが新しいpropsを受け取る前 |
shouldComponentUpdate | 新しいpropsやstateを受け取った時 |
componentWillUpdate | 新しいpropsやstateを受け取った後、レンダリングする直前 |
componentDidUpdate | 更新が行われた直後 |
コンポーネントが破棄される期間のことをUnMountingの期間といいます。 componentWillUnmountというライフサイクルメソッドが、コンポーネントが破棄される直前に呼び出されます。
componentDidmountは、次の用途で用いられます。
- Ajaxを使ったデータフェッチを行う(初回)
- DOMに対する処理を行う(初回)
- タイマーをリセットする
- イベントリスナのセット
イベントリスナのセットを行っていきましょう。 LikeButtonコンポーネントを作成して、クリックするといいね数が増えていくという機能を実装します。
import React from 'react';
import LikeButton from './LikeButton';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentDidMount() {
document.getElementById("counter").addEventListener('click', () => this.setState({count: this.state.count + 1}))
}
render() {
return (
<div>
<LikeButton count={this.state.count} />
</div >
)
}
}
export default App
import React from 'react';
const LikeButton = (props) => {
return (
<div>
<button id="counter">いいね数: {props.count}</button>
</div>
)
}
export default LikeButton
また今回の例では componentDidMount()は用いずにApp.jsxからpropsとしてcountを更新する関数を渡し、それを子供のコンポーネントのボタンタグのonClickに指定することでも実装できます。
このライフサイクルメソッドは、stateがある閾値を超えたときに別の動作をするようにしたいときなどに実装すると便利です。 componentDidUpdateはstateが更新され、それによりrenderが走った後に呼び出されます。
今回は、いいね数が5という閾値を超えた際に、いいね数を0にするという機能を実装していきます。
import React from 'react';
import LikeButton from './LikeButton';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentDidMount() {
document.getElementById("counter").addEventListener('click', () => this.setState({count: this.state.count + 1}))
}
componentDidUpdate(){
if (this.state.count >= 5) {
this.setState({
count: 0
})
}
}
render() {
return (
<div>
<LikeButton count={this.state.count} />
</div >
)
}
}
export default App
import React from 'react';
const LikeButton = (props) => {
return (
<div>
<button id="counter">いいね数: {props.count}</button>
</div>
)
}
export default LikeButton
このメソッドは主に次の機能を実装したいときに使います。
- タイマーを解除する
- イベントリスナーを解除する
- 非同期処理を中止する
今回は、イベントリスナーを解除してメモリを開放する機能を実装します。
import React from 'react';
import LikeButton from './LikeButton';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentDidMount() {
document.getElementById("counter").addEventListener('click', () => this.setState({count: this.state.count + 1}))
}
componentDidUpdate(){
if (this.state.count >= 5) {
this.setState({
count: 0
})
}
}
componentWillUnmount() {
document.getElementById('counter').removeEventListener('click', () => this.setState({count: this.state.count + 1}))
}
render() {
return (
<div>
<LikeButton count={this.state.count} />
</div >
)
}
}
export default App
これでコンポーネントがアンマウントされたときにイベントリスナーを削除して、メモリを開放することができるようになりました。
Hookとは、端的に言えばクラスコンポーネントの機能を関数コンポーネントでも使えるようにするためのものです。
クラスコンポーネントには以下で示すような問題がありました。
- 処理が散らばりやすい
- thisを使う必要がある
- 複数のライフサイクルメソッドに処理がまたがるのが嫌だ
- クラスコンポーネントの構文が複雑
- stateの扱い方が複雑
Hooksでstateを用いるときは、useStateというメソッドを使います。
今回は、ScissorsコンポーネントというFunctionalコンポーネントを作成して、それを親コンポーネントであるAppコンポーネントから呼び出します。 Scissorsコンポーネントにcuttingとうstateを持たせて、ボタンを押すことで変化させるという機能を実装します。
App.jsx
import React from 'react';
import Scissors from './Scissors'
const App = () => {
return(
<div>
<Scissors />
</div>
)
}
export default App
scissor.jsx
import React, { useState } from 'react';
const Scissors = () => {
const [cutting, changeCutting] = useState("NoCutting");
return(
<div>
<h1>{cutting}</h1>
<button onClick={() => changeCutting("YesCutting!")}>change </button>
</div>
)
}
export default Scissors
Hooksではライフサイクルメソッドの代替としてuseEffectメソッドがあります。 クラスコンポーネントにおける以下3つの代替です。
- coponentDidMount()
- componentDidUpdate()
- componentWillUnmount()
componentDidMountはマウントが行われた直後に実行されるメソッドです。 Ajaxを使ったデータフェッチ(初回)やイベントリスナのセットなどを行います。
componentDidUpdateはstateの更新が行われた直後に行われるメソッドです。 stateがある閾値を超えたときに別の動作をするようにしたいときなどに使うと便利です。
componentWillUnmountは、コンポーネントが破棄される直前に呼び出されます。 イベントリスナーを解除してメモリを開放するときなどに利用されます。
useEffect(() => {
console.log('mounting')
}, [])
第二引数に空の配列が渡された場合、マウント時のみ、第一引数に渡されたコールバック関数を実行します。 Reactはrenderが呼ばれるたびに第二引数の値がrender前とrender後に変化したかどうかを確認します。
これはcomponentDidMountと同じタイミングになります。
const [boolean, changeBoolean] = useState(true)
useEffect(() => {
console.log('changed')
}, [boolean])
第二引数に値の配列が渡された場合、renderの前後で値が変化した場合に第一引数のコールバック関数が呼ばれます。 第二引数に渡したbooleanの値が変更されるたびに第一引数に渡したコールバック関数が呼ばれるようになります。
useEffectメソッドにおいては、第一引数のコールバック関数内においてreturnを行うと、そのreturnの中の処理がアンマウント時に実行されるようになります。
useEffect(() => {
console.log("render")
return () => {
console.log("Unmounting!")
}
}, []}
第二引数に空の配列を渡しているので、render毎に実行されることはありません。 console.log("render")の部分がマウント時のみ実行されるようになります。 また、return後の関数が、アンマウント時に実行されるようになります。
- 1. モック(サイトの大まかな見た目)をApp.jsに追加します。returnにカッコを追加し、DOM要素を書いていきます。まずは動かなくていいのでサンプルデータを使って全レイアウトを作成します。
- 2. パーツ毎にコンポーネント分離しファイル分けを行い、returnの中に書くDOM要素はApp.jsからコピーします。App.jsを修正します。
- 3. それぞれに動的なパラメータclassコンポーネントならstate等を用いて処理の割り当てを行う。
- 4. 2から3の繰り返しで完成