Menu

Reactコンポーネントとは:関数コンポーネントを例で解説

Reactコンポーネントは、JSXを返すJavaScriptの関数です。関数コンポーネントの書き方と名前の付け方、ツリーへの組み合わせ方、ファイルへの分け方、純粋に保つ方法、そしてコンポーネントと要素の違いを学びます。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

Reactコンポーネントは、JSXを返すJavaScriptの関数です。ボタン、カード、ページ全体などの再利用できるUIの部品です。HTMLタグのように <Profile /> と書いて使い、Reactはその関数を呼んで画面に何を置くかを知ります。アプリは、コンポーネントを別のコンポーネントの中に入れていくことで作られます。

App は Profile を2回描画し、それぞれの Profile が Avatar を描画します。Avatar の中のイニシャルを変えると、両方のプロフィールが更新されます。1つの定義を共有しているからです。今はこのリストの全員が同じ人です。次のページのpropsで、コピーごとに別のデータを渡す方法を紹介します。

コンポーネントを書く

関数コンポーネントには2つの条件があります。

  1. 名前が大文字で始まること。profile ではなく Profile です。
  2. Reactが描画できるものを返すこと。JSX、文字列、数値、null(何も描画しない)、またはそれらの配列です。
function Welcome() {
    return <h1>Welcome back</h1>;
}

// Arrow functions work the same way
const Welcome = () => <h1>Welcome back</h1>;

JSXが複数行にわたるときは、return の後をかっこで囲みます。かっこがないと、改行が続く return は undefined を返し、その後ろはすべて無視されます。

名前を大文字にする理由

JSXはタグの最初の文字を見ます。小文字は組み込みのHTML要素を意味するので、<div> は <div> のままです。大文字は「スコープ内の変数」を意味するので、<Profile /> は自分の Profile 関数を呼びます。コンポーネントに小文字の名前を付けると、Reactは代わりにその名前の未知のHTML要素を作ります。

2行目はラベルしか表示されず、コンソールには Greeting ran しか出ません。Reactはページに空の <greeting> タグを入れ、小文字の関数を一度も呼ばなかったのです。開発ビルドなら The tag <greeting> is unrecognized in this browser という警告も出ますが、プレビューは本番ビルドのように動くので、テキストが欠けていることしかわかりません。関数名を Greetings に、タグを <Greetings /> に変えると直ります。

コンポーネントを組み合わせる

コンポーネントはネストさせるためのものです。ページはツリーで、一番上に App、次にレイアウトの部品、そして1つのボタンのような小さな部品へと続きます。各コンポーネントは自分が直接描画する部品のことしか知らないので、1つずつ考えることができます。

LikeButton はそれぞれ自分のstateを持っているので、1つの投稿にいいねしてもほかの投稿は変わりません。すべての投稿に共有ボタンを付けたければ、それを1回書いて Post の中に <ShareButton /> を追加するだけです。

コンポーネントを分けるタイミング

決まったルールはありませんが、ある部分を独立したコンポーネントにすべきサインがいくつかあります。同じマークアップが2か所に現れる、JSXの一部が自分のstateを持っている(上のいいねボタンのように)、関数が長くなって return を見るのにスクロールが必要になった、などです。こうした区切りで分けると、各コンポーネントが1画面で読める大きさに収まります。早めに分けても損はほとんどありません。コンポーネントはただの関数なので、2つを1つに戻すのも簡単です。

コンポーネントのエクスポートとインポート

ここのエディタは1つのファイルですが、実際のプロジェクトではコンポーネントをそれぞれのファイルに置きます。1つのファイルは、デフォルトエクスポートを1つと、名前付きエクスポートをいくつでも持てます。

// Avatar.jsx
export default function Avatar({ initials }) {
    return <div className="avatar">{initials}</div>;
}

// Badge.jsx
export function Badge({ label }) {
    return <span className="badge">{label}</span>;
}
export function NewBadge() {
    return <Badge label="new" />;
}
// Profile.jsx
import Avatar from './Avatar.jsx';
import { Badge, NewBadge } from './Badge.jsx';

export default function Profile() {
    return (
        <div>
            <Avatar initials="AL" />
            <Badge label="admin" />
            <NewBadge />
        </div>
    );
}

デフォルトエクスポートは任意の名前でインポートでき、名前付きエクスポートは正確な名前を波かっこで囲んでインポートします。チームではたいていどちらかのスタイルに決めて統一します。ファイル名はコンポーネントと合わせるのが一般的で、Avatar.jsx は Avatar をエクスポートします。

コンポーネントを純粋に保つ

Reactは、コンポーネントがレンダリング中に純粋関数のように振る舞うことを前提にしています。同じpropsとstateからは同じJSXが返り、レンダリングはそれ以前に存在していたものを何も変えません。Reactはコンポーネントを、自分では制御できないタイミングで何度もレンダリングすることがあるので、レンダリング中に外側の変数を書き換えるコンポーネントは毎回違う結果を生みます。

純粋でないカップは1、2、3から始まります。ボタンをクリックしてください。お客さんについては何も変わっていないのに、純粋でないリストは4、5、6になります。レンダリングのたびに共有の guest 変数に足していったからです。純粋なカップは番号をpropsから受け取るので、正しいままです。開発環境ではStrict Modeがすべてのコンポーネントを2回レンダリングし、まさにこの種のバグをあぶり出します。プレビューは本番ビルドのように動くので、ここでは再レンダリングしたときにだけずれが見えます。

何かを変えること自体は問題ありません。ただしレンダリング中にしないでください。stateはイベントハンドラで更新し、外の世界(タイマー、購読、ドキュメントのタイトル)とのやり取りはエフェクトで行います。

コンポーネントと要素の違い

コンポーネントは関数、つまり Profile です。要素は、<Profile /> と書いたときにJSXが作るもので、「Profile をこのpropsで描画する」という小さなオブジェクトです。Reactは要素を作り、そこに書かれたコンポーネントを呼び、その結果をDOMに変えます。コンポーネントを Profile() のように自分で呼ぶことはありません。<Profile /> と書いてReactに呼ばせることで、Reactがそのstateとツリー内の位置を追跡できます。

その位置が重要です。次の例は、コンポーネントの中で別のコンポーネントを定義しています。これはよくある間違いです。

何か入力してからボタンをクリックすると、テキストが消えます。App がレンダリングされるたびにまったく新しい NameField 関数が作られるので、Reactはその場所に別の種類のコンポーネントがあると見なし、古いものをそのstateごと捨ててしまいます。NameField を App の上、ファイルのトップレベルに移すと、何度クリックしてもテキストは残ります。Reactが何を残すかをどう決めるかは、レンダリングのページで説明しています。

関数コンポーネントとクラスコンポーネント

React 16.8でフックが登場する前は、stateが必要なコンポーネントはクラスとして書かれていました。

import { Component } from 'react';

class Welcome extends Component {
    render() {
        return <h1>Hello, {this.props.name}</h1>;
    }
}

クラスコンポーネントはReact 19でも動き、古いコードベースや、今でもクラスが必要なエラーバウンダリで目にするでしょう。新しいコードでは、useState や useEffect などのフックを使った関数コンポーネントを使います。同じことをより少ないコードで実現できます。クラスコンポーネントのページでは、クラスの各機能を対応するフックに置き換えて説明しています。

よくある質問

Reactのコンポーネントとは何ですか?

コンポーネントは、JSXを返すJavaScriptの関数として書かれた、再利用できるUIの部品です。HTMLタグのように <Profile /> と書いて使い、Reactはその関数を呼んで何を描画するかを知ります。

Reactのコンポーネント名はなぜ大文字で始める必要があるのですか?

JSXは最初の文字でタグが何かを判断するからです。<profile /> のような小文字のタグは profile という名前のHTML要素として扱われ、自分の関数は一度も呼ばれません。<Profile /> のように大文字で始まるタグが自分のコンポーネントを指します。

関数コンポーネントとクラスコンポーネントの違いは何ですか?

どちらもUIを記述します。関数コンポーネントは普通の関数で、stateやエフェクトにはフックを使います。クラスコンポーネントは React.Component を継承し、render や componentDidMount のようなメソッドを使います。新しいコードでは関数コンポーネントを使います。

Reactの純粋なコンポーネントとは何ですか?

純粋なコンポーネントは、同じpropsとstateに対して同じJSXを返し、レンダリング中に自分の外側のものを何も変えません。Reactはこれを前提に、必要なだけ何度でも問題なくコンポーネントをレンダリングします。

コンポーネントの中で別のコンポーネントを定義できますか?

避けてください。別のコンポーネントの中で定義したコンポーネントはレンダリングのたびに新しい関数になるので、Reactは毎回別のコンポーネントとして扱い、そのstateをリセットします。コンポーネントはファイルのトップレベルで定義してください。

Coddyのプログラミング言語のイラスト

Coddyでコードを学ぼう

始める