React入門 ~基礎編~

公開:2020/09/21
更新:2020/10/18
14 min読了の目安(約12800字TECH技術記事

現在業務で使用している React について、より理解を深めていくために勉強したことを記事へ書くことにしました。
ということで、まずは基礎編です。

この記事は、過去に Qiita および個人ブログへ投稿した記事に一部加筆・修正をしたものです。

記事を書くにあたって

2019年秋頃から Qiita の記事を書いてきましたが、環境構築系ばかりになっている状態でした。
いまだ技術の中身についての記事を書いたことがなかったので、そろそろ書いてみたいなーと思っていました。

技術記事となると、すでに同じテーマで記事を書かれている方がいてクオリティも高くてと、自分なんかが書いてもなと尻込みをしていたのですが、自分の理解を深めるという目的で思い切って書くことにしました。
記事を書く上で自然とまとめるようになるので、頭の中が整理できるので。

もし自分がその技術から離れてしまって、また戻ってきたときに記事を見返して助けになればとも。
まだまだ勉強中の身であるため内容に間違い等ありましたら、コメントいただけると幸いです。

なお、環境構築に関してはここでは書きません。
ただ、過去に Docker で環境構築した記事を書いているので、もしよろしければそちらをご覧ください。
Qiita - DockerでReact + Swagger 環境を作ろう

※現在の自分のスタンスとしては、フロント開発において、わざわざ 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)やライフサイクルを持つことができないため、関数コンポーネントもクラスコンポーネントと同等の機能を有せるようにとrecomposeReact 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;

また、変数定義などがなく純粋に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となります。

条件付きレンダー

論理値 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.png

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を使うようになりました。


React の基礎としてどこまで書くか迷いましたが、公式チュートリアルで扱われたものを中心に今回書きました。
続編として、フォームや Router、Recompose や Redux、React Hooks などについて書いていけたらと。
React Hooks については、現状全く使ったことがない未知の領域ですが、モダンな書き方を追求するためにも今後ぜひ勉強したいです。

参考リンクまとめ

シリーズ記事リンク