現在業務で使用している React について、より理解を深めていくために勉強したことを記事に書くことにしました。
ということで、まずは基礎編です。
※この記事は元々 Qiita からの転載です。
現在は Qiita でなく Zenn の方で更新しています。
目次
記事を書くにあたって #
2019年秋頃から Qiita の記事を書いてきましたが、環境構築系ばかりで、いまだ技術の中身についての記事を書いたことがなかったので、そろそろ書いてみたいなーと思っていました。(ブログで多少技術記事は書いていました)
技術記事となると、すでに同じテーマで記事を書かれている方がいてクオリティも高くてと、自分なんかが書いてもなと尻込みをしていたのですが、自分の理解を深めるという目的で思い切って書くことにしました。
記事を書く上で自然とまとめるようになるので、頭の中が整理できるので。
もし自分がその技術から離れてしまって、また戻ってきたときに記事を見返して助けになればとも。
まだまだ勉強中の身であるため内容に間違い等ありましたら、コメントいただけると幸いです。
なお、環境構築に関してはここでは書きません。
ただ、過去に Docker で環境構築した記事を書いているので、もしよろしければそちらをご覧ください。
→ DockerでReact + Swagger 環境を作ろう
※2020/10/18追記
※現在の自分のスタンスとしては、フロント開発において、わざわざ Docker を使う必要性を感じなくなってきています(Swagger の部分を Docker で構築はありだと考えていますが)
React とは #
React はユーザーインターフェイスを構築するための、宣言型で効率的で柔軟な JavaScript ライブラリです。複雑な UI を、「コンポーネント」と呼ばれる小さく独立した部品から組み立てることができます。
※React - チュートリアル:React の導入 - React とは? より
React (リアクト) は、Facebook とコミュニティによって開発されているユーザインタフェース構築のための JavaScript ライブラリである。React.js またはReactJS の名称でも知られている。
React はシングルページアプリケーションやモバイルアプリケーションの開発におけるベースとして使用することができる。
※Wikipedia - React より
React の基本 #
以下、記述しているコードは React 公式ドキュメントや公式チュートリアルを引用、もしくはベースにしています。
基本文法 #
index.js で、ReactDOM.render
でレンダリングするコンポーネントと、レンダリングする箇所を定義。
この例では、App というコンポーネントに定義された React 要素を、ルート DOM ノード(index.html の id が root の要素)にレンダリングするよう定義しています。
public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root')); // ルートDOMノードにAppコンポーネントをレンダリングするよう定義
コンポーネント定義の中の return で返される React 要素(JSX)がレンダリングする内容。
(※関数コンポーネントのため省略されていますが、正確にはrender()
メソッドが返す React 要素のこと)
エクスポートすることで、他ファイルからこのコンポーネントを呼び出すことができるようになります。
src/App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
return ( // レンダリングする内容
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App; // コンポーネントをエクスポート
コンポーネント #
クラスコンポーネント #
React.Component
を継承したクラスで定義されたコンポーネント。
後述する状態(state)やライフサイクルを持つことができます。
import React from 'react';
class App extends React.Component { // React.component を継承するクラスの定義
render() { // JSX を戻り値とする render メソッドを定義
return (
<h1>Hello World</h1>
);
}
}
export default App;
関数コンポーネント #
クラスコンポーネントと違い、状態(state)やライフサイクルを持たないコンポーネントをよりシンプルに記述したもの。
const で変数定義などはできますが、基本的にrender()
メソッドのみであるため、render()
の記述が省略できます。
最近の傾向では、なるべく関数コンポーネントを使用する方向であるようです。
ただ、状態(state)やライフサイクルを持つことができないため、関数コンポーネントもクラスコンポーネントと同等の機能を有せるようにとrecompose
やReact Hooks
が生まれたそうです。
import React from 'react';
function App() {
return (
<h1>Hello World</h1>
);
}
export default App;
アロー関数で以下のようにも書くことができます。
import React from 'react';
const App = () => {
return (
<h1>Hello World</h1>
);
}
export default App;
※2020/2/1追記
また変数定義などがなく、純粋にrender()
メソッドのみの場合は、さらにreturn
も省略できます。
import React from 'react';
const App = () => (
<h1>Hello World</h1>
)
export default App;
JSX #
基本 #
コンポーネントの render() メソッド内に記述する HTML と似た構文で、React 要素を生成するもの。
Babel でのコンパイル時にReact.createElement()
形式へ変換されます。
そのため、以下のコードは等価のものになります。
const element = (
<h1 className="title">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'title'},
'Hello, world!'
);
また、JSX では1つのReact要素を返す必要があります。
複数の要素から構成される場合は、div で囲うなどして、1つの React 要素を返すようにしましょう。
(※div などでは支障が出る場合は、React.Fragment の欄を参照)
NG な例
render() {
return (
<h1>h1です</h1>
<h2>h2です</h2>
<p>pです</p>
)
}
OK な例
render() {
return (
<div>
<h1>h1です</h1>
<h2>h2です</h2>
<p>pです</p>
</div>
);
}
JSX は HTML と似ていますが閉じタグに関しては注意が必要。
img タグのように HTML では閉じタグが必要なかったものについては、末尾に/
とつけて閉じる必要があります。
<img src='画像URL' />
React.Fragment #
場合によっては、複数要素をまとめるために div で囲うことで支障が出ることもあります。
そういったときはReact.Fragment
というものを使うことができ、DOM に余分なノードを追加することなく子要素をまとめられます。
また、React.Fragment
では後述するkey
プロパティを持つことができます。
詳しくはこちら
render() {
return (
<React.Fragment>
<h1>h1です</h1>
<h2>h2です</h2>
<p>pです</p>
</React.Fragment>
);
}
短縮記法で以下のようにも書くことができますが、こちらではkey
プロパティを持つことができないので注意が必要です。
render() {
return (
<>
<h1>h1です</h1>
<h2>h2です</h2>
<p>pです</p>
</>
);
}
変数の埋め込み #
JSX 内に変数を埋め込む際は{}
で囲って記述します。
const name = 'Yamada Taro';
const element = <h1>Hello, {name}</h1>;
なお、デフォルトでは、React DOM は JSX に埋め込まれた値をレンダリングされる前にエスケープします。
そのため、以下のようにユーザーの入力を受け付ける場合でも、XSS などインジェクション攻撃を防ぐことができます。
const title = response.potentiallyMaliciousInput;
const element = <h1>{title}</h1>;
命名規則 #
要素のプロパティに関してはキャメルケース。
例として、class はclassName
となり、tabindex はtabIndex
となります。
条件付きレンダー(※2020/2/1追記) #
論理値 or 論理式と&&の組み合わせで、条件によってレンダリングする内容の切り替えができます。
render() {
const flg = true;
return (
<div>
{/* flg変数がtrueの時のみレンダリング */}
{flg && <p>フラグ:true</p>}
{/* flg変数がfalseの時のみレンダリング */}
{!flg && <p>フラグ:false</p>}
</div>
)
}
三項演算子を使用した例
render() {
const flg = true;
return (
<div>
{flg ? <p>フラグ:true</p> : <p>フラグ:false</p>}
</div>
)
}
props #
基本 #
コンポーネントに渡せるプロパティのこと。
props を使うことで親コンポーネントが子コンポーネントに情報を渡すことができ、同じコンポーネントでも、渡す props によって変化をつけることができます。
呼び出し側
<Message name="Taro" />
コンポーネント側(関数コンポーネントの例)
function Message(props) {
return <h1>Hello, {props.name}</h1>;
}
コンポーネント側(クラスコンポーネントの例)
class Message extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
children #
子要素を意味する props。
タグやコンポーネントの中身を children で指定する書き方もできます。
そのため、以下のコードは等価になります。
<h1>test</h1>
<Message>test</Message>
<h1 children="test" />
<Message children="test" />
props として渡されるので、コンポーネント側で使用できます。
function Message(props) {
return <h1>{props.children}</h1>
}
state #
基本 #
クラスコンポーネントでは state(状態)を持つことができます。
設定するには、まずクラスコンポーネントにコンストラクタを追加して state を初期化。
(クラスコンポーネントのコンストラクタは常に props を引数として、親クラスのコンストラクタを呼び出す必要があります)
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null,
};
}
※以下省略
}
state の更新 #
直接 state に代入するのでなく、setState
を使用して更新するようにします。
なお、一度のsetState
で複数の state の値更新も可能です。
state が更新されると、そのコンポーネントは再度レンダリングされます。
(setState
を使用せずに state を直接変更した場合は再レンダリングされないのでやらないこと)
class Square extends React.Component {
※一部省略
render() {
return (
// ボタンクリックでstateに値をセットする
<button className="square" onClick={() => this.setState({value: 'X'})}>
{this.state.value}
</button>
);
}
}
また、this.props と this.state は非同期に更新されるため、setState
時にそれらの値に依存してはいけません。
ダメな例
this.setState({
counter: this.state.counter + this.props.increment,
});
子コンポーネントから親コンポーネントの state を変更したいという時、this.state
はそのコンポーネント内でプライベートになるため、直接変更はできません。
この場合は、親コンポーネントから子コンポーネントに state を更新する関数を渡すようにして、この関数を子コンポーネントから呼び出すことで対処できます。
class Square extends React.Component {
※一部省略
render() {
return (
<button className="square" onClick={() => this.props.onClick()}>
{this.props.value}
</button>
);
}
}
class Board extends React.Component {
※一部省略
handleClick(i) {
// 配列のコピーを作成
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares: squares});
}
renderSquare(i) {
return <Square value={this.state.squares[i]} onClick={() => this.handleClick(i)}/>;
}
}
イベント #
基本 #
基本的にはタグ内にイベント名={() => {処理}}
で記述できます。
<button onClick={() => {console.log('Hello World')}}>こんにちは</button>
イベントの例
- onClick クリックされた時、button タグなど
- onSubmit 送信された時、form タグなど
- onChange 入力や削除が行われた時、input タグなど
- onMouseOver マウスカーソルが上に置かれた時
- onMouseOut マウスカーソルが外れた時
onChange の例
event
は合成 (synthetic) イベントです。event.target.value
で入力された値を取得できます。
<input onChange={(event) => {console.log(event.target.value)}} />
preventDefault #
a タグのリンクやチェックボックスなど、元々のイベントをキャンセルするものです。
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
key #
要素のリストをレンダリングする際、リストの項目それぞれについて、React は情報を保持します。
リストに変更、追加、削除などがあった時に、React がどのアイテムが変更になったのかを知るために、key プロパティを与えるようにしましょう。
リストが再レンダリングされる際、React はそれぞれのリスト項目の key について、前回のリスト項目内に同じ key を持つものがないか探します。
その結果によってリスト項目を追加したり、削除したりします。
key プロパティの値は兄弟要素の中で一意であれば問題ないようです。
なお、リスト項目に key プロパティを設定していないと、コンソールで警告が表示されます。
また、key プロパティは props の一部のようにも見えますが、this.props.key
で参照はできません。
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
リスト項目をコンポーネント化した際は、呼び出し時に key プロパティを設定するようにしましょう。
function ListItem(props) {
// ここではkeyプロパティを指定しない
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// ここでkeyプロパティを指定
<ListItem
key={number.toString()}
value={number}
/>
);
return (
<ul>
{listItems}
</ul>
);
}
ライフサイクル #
ライフサイクルの流れ図
※React Lifecycle Methods diagram より(GitHub - react-lifecycle-methods-diagram)
マウント #
コンポーネントのインスタンスが作成されて DOM に挿入される時、以下のメソッドが次の順序で呼び出されます。
- constructor()
- static getDerivedStateFromProps()
- render()
- componentDidMount()
更新 #
props や state の変更によって発生します。
コンポーネントが再レンダリングされるときに、以下のメソッドが次の順序で呼び出されます。
- static getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
アンマウント #
コンポーネントが DOM から削除されるときに呼び出されます。
- componentWillUnmount()
エラーハンドリング #
任意の子コンポーネントのレンダリング中、ライフサイクルメソッド内、またはコンストラクタ内でエラーが発生したときに呼び出されます。
- static getDerivedStateFromError()
- componentDidCatch()
それぞれのメソッドの解説 #
長くなりそうなので割愛します。
こちらで詳しく解説されていますのでご参照ください。
なお、自分はrender()
後に一度だけ呼ばれるcomponentDidMount()
を使用することが多いです。(といいつつ、最近まで呼ばれるタイミングを少し勘違いしていたのですが…)
→ 最近はuseEffect
を使うようになりました(2020/10/18現在)
React の基礎としてどこまで書くか迷いましたが、公式チュートリアルで扱われたものを中心に今回書きました。
続編として、フォームや Router、Recompose や Redux、React Hooks などについて書いていけたらと。
React Hooks については、現状全く使ったことがない未知の領域ですが、モダンな書き方を追求するためにも今後ぜひ勉強したいです。
参考リンクまとめ #
- React - Doc
- React - Tutorial
- Wikipedia - React
- React(v16.4) コンポーネントライフサイクルメソッドまとめ
- Stateless な React Component の記法をまとめてみた
- React Lifecycle Methods diagram
- GitHub - react-lifecycle-methods-diagram
シリーズ記事 #
- React入門 ~基礎編~ ※当記事
- React入門 ~React Router編~
- React入門 ~PropTypes編~
- React入門 ~Material UI編~
- React入門 ~Recompose編~