詳細検索

関心事の分離:Reactコンポーネントをコンテナとビューに分割する

アバター
著者: Chen Ziyu

関心事の分離:Reactコンポーネントをコンテナとビューに分割する
Englishから翻訳 • 原文を読む

多くのフロントエンドエンジニアは、Reactのコンポーネントがあまりにも巨大になりすぎて、フロントエンドのコードを操作する際に思考の流れを見失ってしまうという混乱を経験したことがあるでしょう。可読性を保つために最も一般的な手法の一つは、巨大なReactコンポーネントを小さな部分に分割することです。従来はJSX要素を分割することで行っていました。この方法は、データの表示だけを担当するReactコンポーネントの可読性を効果的に向上させることができます。しかし、データの表示に加えて、一部のReactコンポーネントはバックエンドからのデータ取得も担当しています。これらのReactコンポーネントを読みやすくするより効果的な方法は、コンテナとビューに分けることです。この記事では、関心事の分離の原則に基づいてReactコンポーネントをコンテナとビューに分割する方法を実演します。

コンテナとビューとは何か?

まず、コンテナとビューがそれぞれ何を担当しているのかを明確にします。「コンテナ」コンポーネントはデータの取得と状態の維持を担当します。データの表示には全く関与していません。一方、「ビュー」コンポーネントの唯一の責任はデータの表示です。バックエンドからデータを取得する方法を気にする必要はありません。

ご存知の通り、「コンテナ」コンポーネントと「ビュー」コンポーネントの間には明確な関心点が分かれています。このようなアプローチは、全体のコードの可読性を向上させるのに役立ちます。「コンテナ」コンポーネントを見る際には、データの取得方法と更新方法に集中できます。「ビュー」コンポーネントを進める際には、ユーザーにデータをどのように提示するかにレーザーフォーカスを向けることができます。

コンポーネントを「コンテナ」と「ビュー」に分割する方法は?

このセクションでは、Reactコンポーネントを「コンテナ」と「ビュー」コンポーネントに分割する方法を簡単な例で示します。例えばページレベルのコンポーネントがあるとします。これはバックエンドからユーザー情報を取得し、ユーザー情報を表示し、ユーザーがニックネームを変更できるようにします。

 

import { useEffect, useState } from 'react';
インポートスタイルは「./UserPage.module.css」から;

import { useEffect, useState } from 'react';
インポートスタイルは「./UserPage.module.css」から;

タイプ User = {
  id: 番号;
  ファーストネーム:文字列;
  姓:string;
  メール:string;
  avatarUrl: string;
  ニックネーム:ストリング;
};

type UserPageProps = {
  ユーザーID: number;
};

export function UserPage({ userId }: UserPageProps) {
  const [user, setUser] = useState(null);
  const [newNickname, setNewNickname] = useState('');

  空の依存配列を「useEffect」フックに渡すと、コンポーネント作成時に渡した関数は一度だけ実行されます。
  useEffect(() => {
    fetch('/api/v1/users/${userId}', {
      方法:「GET」
      ヘッダー: {
        'Content-Type': 'application/json',
      },
    })
      .then((res) => {
        返res.json();
      })
      .then((user: User) => {
        setUser(user);
      });
  }, []);

  const handleNicknameSubmit = () => {
    setUser(null);
    fetch('/api/v1/users/${userId}/nickname', {
      メソッド:「POST」
      ヘッダー: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        ニックネーム:newNickname、
      }),
    })
      .then((res) => {
        返res.json();
      })
      .then((user: User) => {
        setUser(user);
      });
  };

  ユーザーに戻る? (
    <>
      
        名前:{user.firstName}
        姓:{user.lastName}
      
      
        
      
      
        ニックネーム: 
         {
            setNewNickname(e.target.value);
          }}
        />
        提出
      
    
  ) : (
    読み込み中...
  );
}

このページレベルのコンポーネントはまず、マウント中の現在のユーザー情報を取得するために '/api/v1/users/${userId}'' に「GET」リクエストを送ります。その後、このコンポーネントはその情報を受信すると表示されます。このコンポーネントには、現在のユーザーがニックネームを変更するための入力機能も備えています。ユーザーが「Submit」ボタンをクリックすると、現在のユーザーのニックネームを更新するために「/api/v1/users/${userId}/nickname' への「POST' リクエストをトリガーします。

私のキャリアを通じて、このようなページレベルの要素を多く見てきました。中にはさらに複雑なものもあります。それらはすべてを網羅しており、すべてを一か所で見つけることができます。しかし、同じ場所にあまりにも多くのことが詰まっているため、読むときに迷いやすいのも一つの方法です。

次に、このページレベルのコンポーネントを「コンテナ」と「ビュー」コンポーネントに分割して、読みやすくしましょう。

 

import { useEffect, useState } from 'react';
インポートスタイルは「./UserPage.module.css」から;

import { useEffect, useState } from 'react';
インポートスタイルは「./UserPage.module.css」から;

タイプ User = {
  id: 番号;
  ファーストネーム:文字列;
  姓:string;
  メール:string;
  avatarUrl: string;
  ニックネーム:ストリング;
};

type UserPageViewProps = {
  ユーザー:ユーザー;
  newニックネーム:string;
  setNewNickname: (newNickname: string) => void;
  handleNicknameSubmit: () => void;
};

function UserPageView({
  ユーザー、
  newNickname、
  setNewNickname、
  handleNicknameSubmit、
}: UserPageViewProps) {
  戻り(
    <>
      
        名前:{user.firstName}
        姓:{user.lastName}
      
      
        
      
      
        ニックネーム: 
         {
            setNewNickname(e.target.value);
          }}
        />
        提出
      
    
  );
}

type UserPageContainerProps = {
  ユーザーID: number;
};

export function UserPageContainer({ userId }: UserPageContainerProps) {
  const [user, setUser] = useState(null);
  const [newNickname, setNewNickname] = useState('');

  useEffect(() => {
    fetch('/api/v1/users/${userId}', {
      方法:「GET」
      ヘッダー: {
        'Content-Type': 'application/json',
      },
    })
      .then((res) => {
        返res.json();
      })
      .then((user: User) => {
        setUser(user);
      });
  }, []);

  const handleNicknameSubmit = () => {
    setUser(null);
    fetch('/api/v1/users/${userId}/nickname', {
      メソッド:「POST」
      ヘッダー: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        ニックネーム:newNickname、
      }),
    })
      .then((res) => {
        返res.json();
      })
      .then((user: User) => {
        setUser(user);
      });
  };

  もし(!user) {
    戻る 読み込み中...;
  }

  戻り(
    
  );
}

上記のコードでご覧の通り、ユーザー情報表示用のコードを抽出し、新しい「View」コンポーネント「UserPageView」にしました。ユーザー情報の取得やニックネーム変更のためのAPI呼び出しのロジックは、元のページレベルのコンポーネントに残し、改名して「UserPageContainer」に改名しました。「UserPageView」コンポーネント作業では、表示するデータや要素のスタイリングに注力できます。「UserPageContainer」を調整する際は、状態のメンテナンスやユーザー情報の取得・更新に関係ないことに気を取られることはありません。全体として、このページのフロントエンドコードを読みやすくリファクタリングしやすくなります。

この記事では、まずコンテナとビューの定義から始めました。次に、Reactのコンポーネントをコンテナとビューに分けることで可読性が向上する理由を説明しました。最後に、Reactコンポーネントをコンテナとビューに分離する方法を具体的な例で示しました。この技術でReactコンポーネントをリファクタリングするのは時間がかかるかもしれませんが、長期的にはあなたや同僚の作業効率を向上させることができます。時間があるときにぜひこのリファクタリング技術を試してみてください!

Related Articles