JavaScript
 Computer >> コンピューター >  >> プログラミング >> JavaScript

Reactコンポーネント徹底解説:関数コンポーネントとクラスコンポーネントの違い

React.jsは、開発者がコードをカプセル化して再利用性を高めることを可能にするJavaScriptフレームワークです。こうしたカプセル化されたコードの断片はコンポーネントと呼ばれ、Document Object Model(DOM)で起こっていることに干渉することなく、独自のロジックと状態(state)を保持できます。

Webサイトの各部分を小さな単位のコンポーネントに分割し、そこでデータを受け渡すことで、コードは再利用可能になり、DRY(Don't Repeat Yourself=繰り返しを避ける)な設計を実現できます。Reactで扱うコンポーネントには主に2種類あります。それが関数コンポーネントクラスコンポーネントです。


大枠で捉えると、Reactコンポーネントとは、propsを引数として受け取り、画面に表示すべき内容を記述するReact要素を返すJavaScript関数のようなものです。

import React from 'react';
import ReactDOM from 'react-dom';
 
const Greeting = (props) => {
   return (
       <div>
           Hello, {props.name}
       </div>
   );
};
 
const element = <Greeting name="Yolanda" />;
 
ReactDOM.render(
   element, document.getElementById('root')
)

この例では、React要素はテキストを含む<div>です。このテキストは、コンポーネントに渡されたpropsオブジェクトを利用しています。propsオブジェクトは、親コンポーネントから子コンポーネントへとデータを受け渡す仕組みです。

ここで渡されているpropはnameであり、elementが親に相当します。

<Greeting />コンポーネントに渡した任意のプロパティはpropsオブジェクトに格納され、Greeting内部で利用できるようになります。任意の名前を渡せるため、Greetingは非常に再利用性の高いコンポーネントになります。

関数コンポーネント

前述のとおり、関数コンポーネントは基本的にJavaScript関数です。Reactコンポーネントの作成には、ECMAScript 5(ES5)またはECMAScript 6(ES6)の構文を使用できます。また、Reactのルールとして、コンポーネント名は必ず大文字で始める必要があります。これにより、それがコンポーネントであることを明示します。

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Basic React Component</title>
</head>
<body>
   <!-- App is inserted at the root node -->
   <div id="root"></div>
  
   <!-- React CDN -->
   <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
   <!-- React-DOM CDN -->
   <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
  
   <!-- React Components can be rendered here -->
   <script async>
       'use strict';
 
       const element = React.createElement;
 
       function Greeting() {
           return element('h4', null , `Hello, World!`);
       };

       function App() {
           return element(Greeting);
       };
 
       const domContainer = document.querySelector('#root');
           ReactDOM.render(element(App), domContainer);
   </script>
</body>
</html>

これは、JSXを使わずプレーンなJavaScriptだけで書いたES5の関数コンポーネントの実用的なサンプルです。HTML部分はあまり気にせず、JavaScriptのロジックに注目してください。要素を生成し、何かを返す関数を定義し、その関数をReactDOMにレンダリングしている、という流れになっています。

JSX(JavaScript XML Extensionの略)を使うと、JavaScriptの中にHTML形式でUIを記述でき、普段HTMLで見慣れた書き方に近づけられます。上のコード例では、純粋なJavaScriptでHTML要素を生成し、ReactのcreateElementメソッドを使って<h4>をDOMの<div id="root">に挿入しています。

しかし、アプリケーションを大規模に拡張していくと、この書き方はすぐに煩雑になります。JSXは本質的にはReact.createElementメソッドのシンタックスシュガーとして考案されたものであり、アプリをより速くスケールさせることを可能にしました。

関数コンポーネントはES5でもES6でも記述できます。以下は、JSXを使ったES5とES6それぞれの関数コンポーネントの例です。

ES5の関数コンポーネント

import React from 'react';
import ReactDOM from 'react-dom';
 
function Animal(props) {
           const element = <h1>My cat's name is {props.name}</h1>;
           return element;
      }
       function App() {
               return <Animal name="Marshmallow"/>
       };
      const domContainer = document.querySelector('#root');
          ReactDOM.render(<App/>, domContainer);

ES6の関数コンポーネント

const Animal = (props) => {
           const element = <h1>My cat's name is {props.name}</h1>;
           return element;
      }
       const App = () => {
               return <Animal name="Marshmallow"/>
       };
      const domContainer = document.querySelector('#root');
          ReactDOM.render(<App/>, domContainer);

React v.16より前、関数コンポーネントはステートレスコンポーネントと呼ばれていました。つまり、その役割は主に見た目の表現(プレゼンテーション)に限定され、ページ上に適切に表示することだけを担っていました。多くの場合、データは親から渡され、それをユーザーインターフェースに表示するだけでした。

React v.16の登場により、この状況は一変しました(詳細は後述)。現時点では、関数コンポーネントは「親やグローバルオブジェクトからデータを受け取り、クライアントに何かを表示するために存在する」ということだけ覚えておけば十分です。

クラスコンポーネント

クラスコンポーネントは、「単なる」JavaScript関数より少しだけ複雑です。Reactのクラスコンポーネントは、ES6のJavaScriptクラスの概念を採用しています。JavaScriptにおけるクラスとは、基本的に属性とそれに関連付けられたメソッドを持つオブジェクトの設計図であり、thisキーワードを使ってそのインスタンスにアクセスし、操作します。

Reactでも構造はほぼ同じです。Reactのクラスコンポーネントはオブジェクトのインスタンスであり、このオブジェクトにはstate(状態)と呼ばれるものが備わっています。大まかに言えば、stateとはコンポーネントが保持するデータのことであり、属性を設定してクラスに結び付けるもう一つの方法と考えてよいでしょう。開発者はそのデータを使って、画面への表示、他のコンポーネントへの受け渡し、他のロジックへの活用など、自由に処理を行えます。

ES6のクラスコンポーネントも、関数コンポーネントと同様に必ず大文字で始めます。トランスパイラに「これはコンポーネントだ」と認識させるためのReactのルールです。また、React本体からコンポーネントの構造を継承するため、Componentクラスをextends(継承)する必要があります。stateはそのコードブロック内に定義します。

import React from 'react';
import ReactDOM from 'react-dom';
import Name from './Name';
 
   // クラスのインスタンスを参照するときは'this'キーワードが必要。
   // renderメソッド内でメソッドやstateを参照する際には頭につける。
class Animal extends React.Component {
   constructor(props) {
       super(props);
       this.state = {
           species: ['fox', 'tiger', 'bear', 'kangaroo', 'fish', 'cat', 'dog', 'squirrel']
       }
   }
  
   render() {
       return (
           <div>
               {this.state.species.map(animal => {
                   return <Name animal={animal}/>
               })}
           </div>
       )
   }
}
ReactDOM.render(<Animal />, document.getElementById('root'));

stateはプロパティと値の集まりであるオブジェクトです。この例では、speciesというプロパティに動物名の配列が格納されています。この配列を参照・操作するには、this.state.speciesを使用します。

上記のクラスコンポーネントの目的は、動物の名前を<Name />コンポーネントへ受け渡すことです。<Name />側の役割は、受け取ったデータに対して何らかの処理を行うことです。Animalのstateは、関数コンポーネントや他のクラスコンポーネントへ渡される際にpropsオブジェクトの一部になります。渡され続ける限り、子孫コンポーネントからアクセス可能です。

重要なポイントとして、Reactではデータは親コンポーネントから子コンポーネントへ、1階層ずつ下方にのみ流れるという点を覚えておいてください。親の孫コンポーネントでデータが必要な場合は、さらに1階層下へ渡す必要があります。

クラスコンポーネントのもう一つの特徴は、stateを管理するためにライフサイクルメソッドへアクセスできることです。Reactのライフサイクルには通常3つのフェーズがあります。生成(マウント)、生存(更新)、消滅(アンマウント)です。各フェーズでstateへアクセス・変更するためのメソッドが用意されています。

  1. componentDidMount() - AJAX通信やネットワークリクエストを行ってstateを初期化するライフサイクルメソッドです。取得したデータをstateへ読み込むにはthis.setState()を使います。
  2. componentDidUpdate() - ユーザーがアプリケーションを操作した後のstate更新は、すべてここで処理されます。
  3. componentWillUnmount() - コンポーネントがアンマウントされる際に実行されるクリーンアップ関数です。タイマーやAJAXリクエストなどの後始末を担います。

ライフサイクルメソッドはこれ以外にも存在しますが、挙げたものが主要なメソッドです。詳細についてはReact公式ドキュメントを参照してください。

最後に、関数コンポーネントのreturn文とは対照的に、クラスコンポーネントではrender()メソッドを使用します。このメソッドは、ReactDOM.render()がAnimalコンポーネントを渡し、Reactがコンストラクタを呼び出した後に実行されます。stateが初期化された後、renderメソッドが呼ばれて、初めて画面にコンテンツが描画されます。

ふう!かなり多くの情報でしたね。

まとめると、React v.16より前の関数コンポーネントは主に表示専用で、stateを扱わず表示だけを行っていました。一方、クラスコンポーネントはすべてのstateロジックを担い、情報を他のコンポーネントへpropsとして渡していました。

関数コンポーネントとuseState()

2018年、ReactはReact Hooksという概念を導入しました。Hooksとは、関数コンポーネント内でライフサイクルメソッドとstateを利用するための、クリーンで簡潔な手法です。

基本の考え方は、これまで解説してきた内容とほぼ同じです。stateがあり、それを操作し、別の場所へ渡す必要がある――主な目的は変わりません。ただし構文は格段にクリーンになり、慣れるまでに少し時間がかかる程度です。

おすすめの学習方法は、まずこのページをブックマークし、クラスコンポーネントで練習を重ねてReactにおけるデータフローの仕組みをしっかり理解してから、戻ってきてステートフルな関数コンポーネントの学習を完了させることです。

それでは、先ほどのコードから始めましょう。

import React from 'react';
import ReactDOM from 'react-dom';
 
const Animal = (props) => {
           const element = <h1>My cat's name is {props.name}</h1>;
           return element;
      }
       const App = () => {
		const [ state, setState ] = useState('Marshmallow');
               return <Animal name={state}/>
 
       };
      const domContainer = document.querySelector('#root');
          ReactDOM.render(<App/>, domContainer);

ここにはAnimalとAppの2つのコンポーネントがあります。AppはAnimalを返し、「Marshmallow」という値を持つnameというpropを渡しています。

意外に思われるかもしれませんが、これをステートフルなロジックへ変換するのはごく簡単です。<App>コンポーネントを見ながら、次の手順に従いましょう。

import React, {useState} from 'react';

- この行でHookであるuseStateをインポートします。

const [ state, setState ] = useState('Marshmallow');

- この行でstateを初期化します。stateとsetStateという名前は任意で、好きな名前を付けられます。慣例としては、値の内容に合わせた名前を付けるのが一般的です。

- stateが実際の状態を表します。初期値はuseState()の括弧内に指定します。

- setStateはthis.setState()に相当するもので、アプリケーションの実行中にstateを更新するためのメソッドです。

               return <Animal name={state}/>

- 「Marshmallow」を{state}に置き換えます。JSX内でJavaScriptを記述する際は波括弧{}を使います。これにより変数を渡せるようになります。

かつてクラスコンポーネントを特徴づけていたReactライフサイクルは、その多彩なメソッド群ごと、すべてを内包するたった一つの基本的なHookへと整理されました!useEffect() Hookひとつで、Reactコンポーネントのマウント・更新・アンマウントをすべて扱えます。

まとめ

React v.16以前、関数コンポーネントは純粋に表示用のビュー層として使われ、stateはクラスコンポーネントからpropsとして渡されたものしか持ちませんでした。クラスコンポーネントはアプリケーションのstateをすべて保持し、データを受け渡す役割を担い、マウント・更新・アンマウントを制御するライフサイクルメソッドを活用していました。そしてReact v.16で登場した新パターンのReact Hooksによって、関数コンポーネントもstateを持てるようになり、独自のライフサイクル処理も実装できるようになりました。

React Hooksが主流になりつつある今でも、あらゆる種類のコンポーネントに精通しておくことは非常に重要です。既存のレガシーコードには今後もしばらくクラスコンポーネントが使われ続けるため、それを読み書きできるよう理解しておく必要があるのです。

  1. React Nativeとは?特徴・仕組み・活用事例をわかりやすく解説

    React Nativeは、Facebook(現Meta)が開発したオープンソースのJavaScriptモバイルフレームワークで、iOSとAndroid向けのネイティブモバイルアプリの構築に特化して設計されています。その基盤となっているのは、モバイルプラットフォーム向けユーザーインターフェースの構築を支援するJavaScriptライブラリ「ReactJS」です。React Nativeは、既存のiOS・Androidアプリに直接組み込んで利用することもできますし、ゼロからネイティブアプリを開発することも可能です。現在では、Facebookモバイルアプリ、Instagram、Pinterest、

  2. C#のConsoleクラスとは?主要プロパティの使い方を実例付きで解説

    C#のConsoleクラスは、コンソールアプリケーションにおける標準入力・標準出力・標準エラー出力の各ストリームを表すクラスです。テキストの表示や入力の読み取りだけでなく、文字色や背景色の変更、カーソル位置やサイズの制御など、コンソール操作に関するさまざまな機能が提供されています。ここでは、Consoleクラスの代表的なプロパティの使い方を、サンプルコードと実行結果とあわせて解説します。Console.CursorLeftプロパティコンソール上のカーソル位置(左端から数えた列位置)を変更するには、Console.CursorLeftプロパティを使用します。サンプルコードusing System