はじめに
自分はReact初心者ながら、数冊の書籍と動画でとりあえずの基礎学習を終えましたので、基礎的な部分を復習して基礎力をつけていこうかなと思います。
Reactとは
Reactとは、Facebookにより開発されたJavaScriptライブラリの一種です。JavaScript単体では開発生産性は低いので、補う手段としてReactなどのライブラリを導入します。
Reactは、コンポーネント
と呼ばれるUI部品を積み上げて、アプリを作っていきます。
コンポーネントについて
コンポーネントとは、UIの一部分となるビュー (View) を切り出したもので、これらを積み上げてアプリケーションを作り上げていきます。
※現在(2021年8月現在)では、コンポーネント作成は関数コンポーネントを利用するのが一般的らしいです(昔はstateがクラス内でしか使えませんでしたが、今はどちらでも可能のため)。
import React from 'react';
// クラスを用いた定義方法
class Greeting extends React.Component {
// render()メソッドは必須
render() {
return <h1>Hello!</h1>;
}
}
// 関数を用いた定義方法
function Greeting() {
return <h1>Hello!</h1>;
}
export default Greeting;
propsについて
Propsはコンポーネント利用時に属性として設定ができる値で、親コンポーネントから子コンポーネントの情報伝達に利用します。props.渡された属性名
としてアクセスできます。
function Hello(props) {
// 渡されたnameはpropsに格納されている
return <h1>Hello! {props.name}!</h1>; // => "Hello! Tanaka!"
}
ReactDOM.render(
// Helloコンポーネントを呼び出すときにnameを渡す
<Hello name="Tanaka" />,
document.getElementById('root')
);
prop-typesについて
prop-typesとは、受け取ったデータが有効かどうかを確認する
ために使用できる種々のバリデーターをエクスポートしています。アプリケーションによっては、Flow
やTypeScript
などJavaScriptを拡張してアプリケーション全体の型チェックを行うことが多いかもしれませんが、ここではReactにもこういった機能があるという紹介です。
無効な値がプロパティに与えられた場合、JavaScript のコンソールに警告文が出力されます。
※パフォーマンス上の理由から、prop-typesのチェックは開発モードでのみ行われます。
import React from 'react';
import PropTypes from 'prop-types';
class Hello extends React.Component {
render() {
return (
<h1>Hello! {this.props.name}!</h1>
);
}
}
Hello.propTypes = {
// 文字列のみ許可
optionalString: PropTypes.string,
// Array型のみ許可
optionalArray: PropTypes.array,
// boolean型のみ許可
optionalBool: PropTypes.bool,
// 関数のみ許可
optionalFunc: PropTypes.func,
// 数値のみ許可
optionalNumber: PropTypes.number,
// オブジェクトのみ許可
optionalObject: PropTypes.object,
// Symbol型のみ許可
optionalSymbol: PropTypes.symbol,
// renderできるもののみ許可
optionalNode: PropTypes.node,
// ReactのElementのみ許可
optionalElement: PropTypes.element,
// 指定したクラスのインスタンスのみ許可
optionalMessage: PropTypes.instanceOf(Message),
// 引数に渡した値のみ許可
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 引数に渡した型の配列のみ許可
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// プロパティが引数に渡した型の値を持つオブジェクトのみ許可
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
// 指定した形のオブジェクトのみ許可
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// 型は任意であるが必須
requiredAny: PropTypes.any.isRequired
};
propsのデフォルト値を設定する場合
defaultProps
というプロパティを割り当てることで、props に値が渡されなかった際のデフォルト値を定義することができます。
class Hello extends React.Component {
render() {
return (
<h1>Hello! {this.props.name}!</h1> //=> "Hello! Default!"
);
}
}
Hello.defaultProps = {
name: 'Default'
};
ReactDOM.render(
<Hello />,
document.getElementById('root')
);
propsをそれぞれ取り出す場合
引数に記載する時点で、渡ってきたpropsは別々で書くことが出来ます。
function Hello!({name, age}) {
return <h1>Hello! {name}! I'm {age} years old!</h1>;
}
...
stateについて
stateとは、コンポーネント利用時に設定ができる値のことで、子コンポーネントから親コンポーネントへの情報伝達に利用します。こちらはpropsとは異なり、変更可能です。
基本的には、constructor()メソッド内でthis.state{}
に値を指定することで、利用できます。
class Hello extends React.Component {
constructor(props) {
// クラスのコンポーネントは常にpropsを引数として親クラスのコンストラクタを呼び出す必要有り
super(props);
this.state = {
// nameプロパティの初期値を設定
name: 'Tanaka'
};
}
render() {
return (
<div>
<h1>Hello! {name}!</h1> // => "Tanaka"
</div>
);
}
}
ReactDOM.render(
<Hello />,
document.getElementById('root')
);
stateを更新する
setState()
を使用することでstateの値を変更することが出来ます。
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Tanaka',
age: 19
};
// ここでメソッドに対してbindをしておく(理由は後に記述)
this.changeAge = this.changeAge.bind(this);
}
// 年齢を+1するメソッドを定義
changeAge() {
this.setState(prevState => ({
// prevStateには前回のstateの値が格納されている
age: prevState.age + 1
}));
}
render() {
return (
<div>
<h1>Hello! {name}! I'm {age} years old!</h1>
<button onClick={this.changeAge}>Change</button> {// ボタンを押すとメソッドが呼び出される}
</div>
);
}
}
ReactDOM.render(
<Hello />,
document.getElementById('root')
);
bindしなければいけない理由
深掘りすると色んな要素が絡み合って少し難しいため、簡単にいうと、イベントハンドラに設定したメソッドのなかで記述したthisが、意図したものとは違うものを指してしまうので、bindを使うことでそれを回避している。
ライフサイクルメソッドについて
コンポーネントを扱う際に、ライフサイクルは大きく分けて3つの期間がある。
Mounting(マウント時)→Updating(更新時)→Unmounting(マウント解除時)となっています。
公式でも下記のような図が紹介されています。
[参考]React lifecycle methods diagram
Mounting(マウント時)
コンポーネントが表示されるまでの期間のこと。①〜④の流れで処理が実行されます。
具体的には以下のようなメソッドがあります。
①constructor()
②static getDerivedStateFromProps()
③render()
④componentDidMount()
Updating(更新時)
コンポーネントが表示されてから、Stateの更新を行うまでの期間のこと。
具体的には以下のようなメソッドがあります。
①static getDerivedStateFromProps()
②shouldComponentUpdate()
③render()
④getSnapshotBeforeUpdate()
⑤componentDidUpdate()
Unmounting(マウント解除時)
現在のコンポーネントを破棄する直前にのみ呼び出されるメソッドのこと。
①componentWillUnmount()
よく使うライフサイクルメソッドの解説
コンポーネントを作成する際によく使うであろうメソッドを解説していきます。
render()
render()メソッドは唯一、クラスコンポーネントで必ず定義しなければならないメソッドです。
ここで返すものが実際にUIに現れるものです。render()メソッドはpropsやstateが更新されるたびに呼び出される(「純粋」である)ため、この中で直接propsやstateを操作してはいけません。
※真偽値またはnullの場合、何もrenderしません。
constructor()
constructor()メソッドは、stateの初期化
やメソッドのbind
などを行います。
React.Componentサブクラスのコンストラクタを実装するときは、必ずsuper(props)
を呼び出す必要があります。
※ ちなみにsetState()は呼び出せません。
...
constructor(props) {
// こちらは必ず呼び出す
super(props);
// stateを初期化する
this.state = { counter: 0 };
// イベントハンドラーをbindする
this.handleClick = this.handleClick.bind(this);
}
...
componentDidMount()
componentDidMount()メソッドは、DOMノードを必要とする初期化などを行います。
データをfetchしたり、タイマーやイベントリスナをセットする時などに利用します。
このメソッド内でsetState()を呼び出す(直接のDOM操作)のは余分なrenderを引き起こす
ので注意しましょう。
componentDidUpdate()
componentDidUpdate()メソッドは、更新が行われた直後に呼び出されます。コンポーネントが更新後、DOMを操作する機会にこのメソッドを使用しましょう。
第一引数に1つ前のprops
、第二引数に1つ前のstate
、第三引数にsnapshot
が入ってきます。(使用しない引数は省略可能です)
こちらのメソッドも、setState()を呼び出す(直接のDOM操作)のは余分なrenderを引き起こす
ので注意しましょう。
...
componentDidUpdate(prevProps) {
// 典型的な使い方(propsを比較すること)
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
...
componentWillUnmount()
componentWillUnmount()メソッドは、コンポーネントがアンマウントされて破棄される直前に呼び出されます。タイマーの無効化
やネットワークリクエストのキャンセル
など、メソッドで必要なクリーンアップを実行します。
コンポーネントは再レンダーされないため、setState()を呼び出してはいけません。
※もうrenderが呼ばれることはないので、ここでpropsやstateを変更しても意味がありません。
JSXについて
JSXとは、JavaScriptの構文拡張したものです。どのようなUIにするかを記述するためにJSXの使用が推奨されています。HTMLと似たような形で使用できるので直感的に扱いやすいのが特徴です。しかし、JSX特有の使い方があるので、そこを押さえておきましょう。
式を埋め込む場合
{}
で囲むことで式や変数を埋め込む事ができます。
const name = 'Josh Perez';
ReactDOM.render(
<h1>Hello, {name}</h1>,
document.getElementById('root')
);
JSXの属性を指定する場合
JSXはHTMLよりJavaScriptに近いので、HTMLの属性ではなくキャメルケースのプロパティ命名規則を使用します。
また、予約語にも気を付けなければいけません。(class→classNameなど)
...
const element = <div tabIndex="0"></div>;
...
タグは必ず閉じなければいけない
HTMLでは閉じタグが不要のタグもありますが、JSXでは必ず閉じタグは必要なので、忘れず閉じましょう。(例のように、閉じ方はどちらでも良いです)
...
const element = <img src={user.avatarUrl}></img>;
<Hello />
...
ルーティングについて
Reactでルーティングを設定する際は、react-router-dom
を利用します。
以下のようにインポートすることで使用する事ができます。
import React from 'react'
import { Link } from 'react-router-dom'
...
基本的な使い方
以下の例のように、インポートしたreact-router-domでPathを設定していきます。
一つずつ説明していきましょう。
import React from 'react'
import { BrowserRouter, Route, Switch, Link } from 'react-router-dom';
// それぞれリンク先のコンポーネントもインポートしておく
import Top from './components/Top';
import Login from './components/Login';
import TodoList from './components/TodoList';
const App = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path='/' component={Top} />
<Route path='/login' component={Login} />
<Route path='/todo' component={TodoList} />
</Switch>
<Link to='/'>Back To Top</Link>
</BrowserRouter>
);
};
export default App;
インポートしたreact-router-domの種類
要素 | 内容 |
---|---|
BrowserRouter | すべてのルーティング要素をラップする |
Switch | Route要素をラップする(上から順に評価される) |
Route | Switch内で定義し、パスネームとページを紐付けする指定を行う |
Link | 通常のリンクを表示する(aタグのようなもの) |
Switch要素について
上から順に評価してマッチしたものを表示します。そのため、リンクの出し分けする際に便利です。また、Route要素に使える属性は以下のようなものがあります。
ルートの評価は前方一致
のため、Route要素の並び順には注意しましょう。
属性 | 内容 |
---|---|
path | 該当のパス |
component | 表示したいページのコンポーネントを指定 |
key | useParams、useRouteMatchで使用 |
exact | pathに指定したルートと完全一致した場合にコンポーネントを返す |
matchについて
このオブジェクトは、ルートのpathとURLのロケーションが一致したときに作成されます。
// matchオブジェクトを渡す
const Drink = ({ match } ) => <p>Drink ID: {match.params.id}</p>;
const App = () => {
return (
<div>
<Link to="/drink/1">coke</Link>
<Link to="/drink/2">tea</Link>
<Link to="/drink/3">coffee</Link>
<Route path="/drink/:id" component={Drink} />
</div>
);
};
matchの中身は以下のようなものになります。
{
// routeのpath
path: "/drink/:id",
// マッチしたURL
url: "/drink/1",
// 完全一致かどうか
isExact: true,
// 渡ってきたパラメーター
params: {
id: "1"
}
}
Tips
Propsを利用した情報の受け渡し方法
Linkコンポーネントにpropsを渡したいときは、render()メソッドを利用して渡してあげます。
import React from 'react'
import { BrowserRouter, Route, Switch, Link } from 'react-router-dom';
import Top from './components/Top';
class App extends Component {
render() {
return (
<div className="App">
<BrowserRouter>
<div>
<Link path='/' render={ () => <Top name={'Tanaka'}/> }/>
</div>
</BrowserRouter>
</div>
);
}
}
export default App;
ルーティングをネストさせる
Switchコンポーネントの中にもう一つSwitchを作成することでネストできます。
import { BrowserRouter, Link, Route, Switch } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Link to="/">Home</Link>
<Link to="/users">Users</Link>
<Switch>
<Route
path="/users"
render={({ match: { url } }) => (
<Switch>
<Route exact path={url}>
<h1>User</h1>
<Link to={`${url}/page1`}>Page1</Link>
<Link to={`${url}/page2`}>Page2</Link>
</Route>
<Route path={`${url}/page1`}>
<h1>User Page1</h1>
</Route>
<Route path={`${url}/page2`}>
<h1>User Page2</h1>
</Route>
</Switch>
)}
/>
<Route path="/">
<h1>Home</h1>
</Route>
</Switch>
</BrowserRouter>
);
}
終わりに
基礎を網羅しようと思ったらかなりボリューミーな記事になっちゃいました。
Reactだけではないですが、フロントエンド界隈のスピード感は半端じゃないので、1年後は古い情報かもしれませんが、現時点からの情報をお届けしました。。
参考
[React公式リファレンス]
(https://ja.reactjs.org/)
[React(v16.4) コンポーネントライフサイクルメソッドまとめ]
(https://qiita.com/yuria-n/items/3c3fc8d29fd2e56ed7a9)
[React Routerでルーティングを実装しよう]
(https://qiita.com/jima-r20/items/c7d636262a9ebf0bedbd)
[【React】ルーティング設定]
(https://zenn.dev/b1essk/articles/react-routing)