SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

バックエンドエンジニアのためのフロントエンド入門

【バックエンドエンジニアのためのReact入門】Reactの関数コンポーネントの基本! クラスとの違いを理解する

バックエンドエンジニアのためのフロントエンド入門 第2回


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

 最後にクラスと関数コンポーネントを比較します。細部に目をつむるとstateはクラスのプロパティ相当であり、状態の更新関数を実行するイベントハンドラはメソッド相当として考えられると思います。

オブジェクト指向のクラス

 第1回で提示した記事クラスのeditTitle()メソッドを思い出してみてください。

editTitle(newTitle: Title) {
  if (this.status === Status.Archived) {
    throw new Error('Archived な記事のタイトルは編集できません');
  }
  this.title = newTitle;
}

 記事ステータスは公開済みとアーカイブ済みの2種類です。

const Status = {
  Published: 'published',
  Archived: 'archived',
} as const;

 editTitle()を呼び出したとき、記事が公開済みならタイトルを編集できますが、記事がアーカイブされていたら編集できません。記事ステータスという名前が示す通り、これは記事の「状態」に応じた挙動なのです。

archive() {
  this.status = Status.Archived;
}

 第1回で例に挙げたArticleクラスをReactのクラスコンポーネントとして表現してみました。本章の冒頭で「state はクラスのプロパティ相当、イベントハンドラはメソッド相当」と書いた意味がわかってもらえるかと思います。

import React, { Component } from 'react';

type ArticleProps = {
  articleId: string;
  title: string;
  content: string;
  author: string;
  status: 'Published' | 'Archived';
  publishedAt: Date;
};

type ArticleState = {
  title: string;
  content: string;
  status: 'Published' | 'Archived';
};

export default class Article extends Component<ArticleProps, ArticleState> {
  constructor(props: ArticleProps) {
    super(props);

    this.state = {
      title: props.title,
      content: props.content,
      status: props.status,
    };
  }

  editTitle(newTitle: string) {
    if (this.state.status === 'Archived') {
      throw new Error('Archived な記事のタイトルは編集できません');
    }
    this.setState({ title: newTitle });
  }

  archive() {
    this.setState({ status: 'Archived' });
  }

  render() {
    const { author, publishedAt } = this.props;
    const { title, content, status } = this.state;

    return (
      <article>
        <header>
          <h2>{title}</h2>
          <p>
            著者: {author}(公開日: {publishedAt.toLocaleDateString()})
          </p>
        </header>
        <p>{content}</p>
        <p>ステータス: {status}</p>
        <div>
          <button onClick={() => this.editTitle('新しいタイトル')}>
            タイトルを編集
          </button>
          <button
            onClick={() => {
              try {
                this.archive();
              } catch (e) {
                alert(e.message);
              }
            }}
            disabled={status === 'Archived'}
          >
            アーカイブする
          </button>
        </div>
      </article>
    );
  }
}

コンポーネント指向のReactコンポーネント

 ここからはもう少し複雑なコンポーネントの説明をします。より実践的なコンポーネントであるため、わからなければ次のクラスとコンポーネントの挙動の比較まで読み飛ばしてもらっても構いません。

 オブジェクト指向のArticleクラスのメソッドが呼び出されると、処理内容はプロパティの値に応じて条件分岐するのと同じようなことをReactコンポーネントで表現してみます。

 以下のArticleWithStatusコンポーネントはstatusという状態と記事を「アーカイブする」ボタンを持ちます。このボタンをクリックすると、コンポーネントのstatusがarchivedに変更され、同時にバックエンドのAPIにリクエストが飛びます。

 アーカイブ済みの記事は「アーカイブする」ボタンを押せないという仕様だとします。ステータスがarchivedの時はそもそもボタンをクリックできないようにしたいため、isArchivedという変数を作ってボタンのdisabled属性に渡します。

type ArticleStatus = 'published' | 'archived';
type Props = {
  articleStatus: ArticleStatus;
};

function ArticleWithStatus({ articleStatus }: Props) {
  // Article クラスの status 相当
  const [status, setStatus] = useState<ArticleStatus>(articleStatus);
  // ハンドラ
  const handleClickArchive = async () => {
    if (status === 'archived') {
      return;
    }

    // status を archived に更新する。
    // Article クラスの Article.archive() 相当
    setStatus('archived');

    // APIサーバーにリクエストを送る
  };

  // アーカイブ状態か確認するフラグ
  const isArchived = status === 'archived'; 

  return (
    <div>
      // ボタンに disabled 属性を付与することにより
      // 「アーカイブする」ボタンは公開済みステータスの時にしか押せないようにする
      <button onClick={handleClickArchive} disabled={isArchived}>
        アーカイブする
      </button>

      <h2>記事タイトル</h2>
      // ...
    </div>
  );
}

 クラスメソッドは、条件分岐でプロパティの値に応じて処理内容を変えることが一般的です。それと同様に、Reactコンポーネントもstateの値が変更されることでJSXを変え、描画されるHTMLを変更するのです。まとめると以下の動きをします。

 ArticleクラスのeditTitle()メソッドの挙動

  • Statusプロパティが published のとき、editTitle() で記事タイトルを変更できる
  • Statusプロパティが archived のとき、editTitle() は例外を投げる

 ArticleWithStatusコンポーネントの挙動

  • statusステートが published のとき、ボタンのdisabledがfalseであるJSXを返す
    • 「アーカイブ」するボタンを押すと、statusステートが archived に更新される(ユーザーのアクションによる再描画)
  • statusステートがarchivedのとき、ボタンのdisabledがtrueであるJSXを返す

 一般的にメソッドを設計するときは次のようなテンプレートに基づいて記述すると良いとされています[2]

  1. 事前条件のチェック
  2. 失敗のシナリオ
  3. ハッピーパス(正常系)
  4. 事後条件のチェック
  5. voidか値を返す

 これをReactコンポーネント風に書くとこのようになるでしょう。

  1. state の設定
  2. イベントハンドラ
  3. プレゼンテーションロジック
  4. JSXを返す

 コンポーネントの役割は「イベントハンドラのロジックとプレゼンテーションのロジック」であるため、この流れがコンポーネント記述の基本です。ただし、他にも副作用やAPI経由のデータフェッチ、フロントエンドでのルーティングなどもあるため、これはあくまで基本です。

まとめ

 次回はAPIサーバーから取得したデータを表示するまでの流れ(データフェッチ)について紹介します。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
バックエンドエンジニアのためのフロントエンド入門連載記事一覧

もっと読む

この記事の著者

プログラミングをするパンダ(プログラミングヲスルパンダ)

 https://twitter.com/Panda_Program/ フロントエンドエンジニア。元々サーバーサイドエンジニアだったが、個人開発を機に HTML, CSS, JS に興味を持つ。特に React、Next.js に熱中しフロントエンジニアに転向。TDD、XP、DevOps が好き。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/20649 2025/04/02 13:21

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー