Reactは、ウェブアプリケーション構築におけるUIの作成に人気のライブラリです。多くの企業で広く利用されており、コミュニティの活動も活発です。

しかし、Reactを使用して、ユーザーフレンドリーで拡張性や保守性に優れたプロジェクトを構築するには、ライブラリの仕組みを理解するだけでは不十分です。

また、クリーンなReactコードを書くには、慣習的な規則の理解も必要です。規則に従うことで、ユーザー体験を向上できるだけでなく、自分自身や他の開発者によるコードの維持も容易になります。

この記事では、まずReact開発者が直面する一般的な課題についてご説明し、次にReactのコードを効率的に記述するためのベストプラクティスをご紹介していきます。

では、早速はじめましょう。

Reactのベストプラクティスについて動画での解説もご用意しています。

React開発者が直面する課題

まずは、React開発者がウェブアプリの構築中や構築後に遭遇する様々な課題についてご説明します。

なおこのセクションで紹介する課題はすべて、後ほどご紹介するベストプラクティスに従うことで回避できます。

まずは初心者がつまづきがちな基本的な課題から見ていきましょう。

Reactの前提条件

React開発における大きな課題の1つがライブラリの動作の理解と、使用に際しての前提条件です。

Reactを扱う前に求められる知識があります。たとえば、ReactはJSXを使用しているため、HTMLとJavaScriptの理解は必須です。そして、ウェブアプリをデザインするにはもちろん、CSSや最近のCSSフレームワークの知識も必要です。

また、Reactを使い始める前に押さえておかなければならない、JavaScriptの中核の概念や機能も。ほとんどがES6に該当し、例えば以下のようなものが挙げられます。アロー関数

  • 残余引数
  • スプレッド演算子
  • モジュール
  • 分割代入
  • Arrayメソッド
  • テンプレートリテラル
  • Promise
  • let変数とconst変数

React初心者であれば、上記の知識を持っていれば、Reactをより効率的に学習しやすくなります。

加えて、以下のようなReact固有の概念についても学習が必要です。

  • コンポーネント
  • JSX
  • ステート/状態管理
  • props
  • 要素のレンダー
  • イベント処理
  • 条件付きレンダー
  • リストとkey
  • フォームとフォームバリデーション
  • フック
  • スタイル

Reactの概念とライブラリを使用する際の前提条件を理解しておけば、Reactの機能を効率的に活用することができます。

求められる知識量に圧倒されてしまう方もいるかもしれませんが、ご安心を。コツコツと練習と学習を重ねていけば、Reactを使って優れたプロジェクトを開発できるようになります。ただし、新たにプログラミング言語を学ぶのと同じように、多少の時間と練習が必要です。

ステート管理

Reactで変数の状態や値を更新する方法は、素のJavaScriptとは異なります。

JavaScriptでの変数の更新はシンプルに等号演算子(=)を使用して、新しい値を代入します。以下に例を挙げます。

var x = 300;
function updateX(){
  x = 100;
}
updateX();
console.log(x);
// 100

上のコードでは、まず初期値300の変数「x」を作成しています。

次に等号演算子を使用して、新しい値100を代入しています。これは、updateX関数の中に記述されています。

Reactでの変数の状態や値の更新は、異なる動作をします。以下がその例です。

import { useState } from 'react';
function App() {
  const [x, setX] = useState(300)
  let updateX =()=>{
    setX(100);
  }
  return (
    <div className="App">
    <h1>{x}</h1>
    <button onClick={updateX}>Update X</button>
    </div>
  );
}
export default App;

Reactで変数の状態を更新するには、useStateフックを使用します。このフックを使用する際には以下の3つの点に注意してください。

  • 変数名
  • 変数を更新する関数
  • 変数の初期値や初期ステータス

以下の例では、xが変数名、setXがxの値を更新する関数で、xの初期値(300)はuseState関数のパラメータとして渡されます。

 const [x, setX] = useState(300)

xの状態の更新にsetX関数を利用します。

import { useState } from 'react';
let updateX =()=>{
  setX(100);
}

つまり、updateX関数がsetX関数を呼び出すことで、xの値が100に設定されます。

この方法は、変数のステートの更新にはうまく動作しますが、大規模なプロジェクトでは、コードが複雑になります。大量にステートフックを使用すると、プロジェクトの規模が大きい場合は、コードの維持と把握の難易度が上がります。

ステートフックの使用における別の問題として、アプリを構成する様々なコンポーネント間で作成した変数が共有されません。ある変数から別の変数にデータを渡すには、propsを利用します。

幸い、Reactには状態管理を効率的に行う各種ライブラリがあります。変数はライブラリを使用して一度作成すればOKで、アプリの任意の場所で使用することができます。ライブラリには、Redux、Recoil、Zustandなどが挙げられます。

状態管理にサードパーティのライブラリを選択する懸念点は、すでに習得したReactの知識とはまた別に、新たな概念の学習を余儀なくされることです。例えば、Reduxは膨大なボイラープレートコードの量で知られ、初心者は敬遠してしまう傾向にあります(ただしこの問題はRedux Toolkitによって解決されつつあり、Reduxよりも少ないコードの記述量で済む)。

保守性と拡張性

変化し続けるユーザー要件に応じて、製品を構成するコードも柔軟に変更しなければなりません。

コードの保守管理が簡単でなければ、コードの拡張がスムーズに行えない可能性も。こうした状況は、悪い慣習に従って書かれたコードが原因で発生します。はじめのうちは問題なく動作し、期待通りの結果が得られているように見えても、将来的にプロジェクトの成長を妨げることになります。

次のセクションでは、Reactのコーディングを改善する規則をご紹介します。この規則は、他の開発者と共同作業を行う際にも有用です。

Reactのベストプラクティス

それでは、本題であるReact開発におけるベストプラクティスを見ていきましょう。

1. フォルダ構造を明確に保つ

フォルダ構造は、自分自身、そして他の開発者がプロジェクトで使用するファイルやアセットの配置を把握するのに役立ちます。

フォルダ構造が整理されていれば、簡単にフォルダ間を移動でき、手間の削減と、混乱の回避につながります。フォルダ構造は、開発者の好みによって異なりますが、以下、一般的なフォルダ構造をご紹介します。

機能またはルートでフォルダをグループ化

機能やルートに応じてフォルダ内のファイルをグループ化することで、特定の機能に関するすべてのファイルを1箇所にまとめることができます。例えば、ユーザーダッシュボードがあれば、ダッシュボードに関連するJavaScript、CSS、テストファイルを1つのフォルダにまとめることが可能です。

例えば、以下の通り。

dashboard/
index.js
dashboard.css
dashboard.test.js
home/
index.js
Home.css
HomeAPI.js
Home.test.js
blog/
index.js
Blog.css
Blog.test.js

上の例において、各アプリの中核機能は、関連するすべてのファイルやアセットを同じフォルダ内に保存しています。

類似ファイルをグループ化

別の方法として、類似のファイルを同じフォルダ内にまとめるのも手です。フックやコンポーネントなどに対しても個別のフォルダを用意し、例えば、以下のようになります。

hooks/
useFetchData.js
usePostData.js
components/
Dashboard.js
Dashboard.css
Home.js
Home.css
Blog.js
Blog.css

もちろん、上記のフォルダ構造に厳密に従う必要はありません。ファイルの配列が決まっているのであれば、それに従ってください。開発者がファイル構造を明確に理解していれば問題ありません。

2. インポート順を構造化する

Reactアプリケーションが成長するにつれ、追加のインポートが必要になります。インポートの構造は、コンポーネントの構成を理解するのに有用です。

慣習として、類似のユーティリティを一緒にグループ化するようにしましょう。例えば、外部やサードパーティのインポートを、ローカルのインポートとは別にグループ化します。

以下はその例です。

import { Routes, Route } from "react-router-dom";
import { createSlice } from "@reduxjs/toolkit";
import { Menu } from "@headlessui/react";
import Home from "./Home";
import logo from "./logo.svg";
import "./App.css";

上のコードでは、まずサードパーティのライブラリ(事前のインストール必須)をグループ化しています。

次に、スタイルシート、画像、コンポーネントなど、ローカルで作成したファイルをインポートしています。

この例では、わかりやすいように短いコードにしていますが、一貫してこのインポート形式を保つことで、Reactアプリの管理が容易になります。

さらに、ローカルファイルはファイルの種類ごとにグループ化可能です。以下のように、コンポーネント、画像、スタイルシート、Hooksなどをローカルインポートの下に個別にグループ化します。

import Home from "./Home";
import About from "./About"
import Contact from "./Contact"
import logo from "./logo.svg";
import closeBtn from "./close-btn.svg"
import "./App.css";
import "Home.css"

3. 命名規則を守る

命名規則を採用すると、コンポーネント名だけでなく変数名やフックにも適用でき、コードの可読性が向上します。

Reactのドキュメントには、コンポーネントの命名に関する公式のパターンが記述されていませんが、最もよく使用される命名規則は、camelCaseとPascalCaseです。

PascalCaseは、主にコンポーネント名に使用されます。

import React from 'react'
function StudentList() {
  return (
    <div>StudentList</div>
  )
}
export default StudentList

コンポーネントはStudentListという名前で、Studentliststudentlistよりも格段に読みやすくなっています。

camelCaseの命名規則は、ほとんど変数、フック、関数、配列などで使用されます。

&const [firstName, setFirstName] = useState("Ihechikara");
const studentList = [];
const studentObject = {};
const getStudent = () => {}

4. リンターを使用する

リンターは、コードの質を上げるのに役立ちます。JavaScriptやReact用のリンターとして人気があるのがESlintです。では、リンターは具体的にどのようにコードの質を改善してくれるのでしょうか。

リンターを使うことで、コードの一貫性が維持できます。ESLintのようなツールを使用すると、プロジェクトに関わるすべての開発者の間に共通のルールを設定可能です。例えば、一重引用符の代わりに二重引用符を使う、アロー関数を中括弧で囲むなど、特定の命名規則を設定できます。

コードを観察し、ルール違反があると通知が出ます。ルールを破ったキーワードや行には、通常、赤の下線が引かれます。

開発者はそれぞれ独自のコーディングスタイルを持っているものですが、リンターを採用することによってコードの統一を図ることができます。

またリンターを使用すると、バグの修正も簡単です。スペルミスや、宣言されているのに使われていない変数を表示する機能などがあり、バグの一部はコードの記述中に自動で修正されます。

ESLintのようなツールは、ほとんどのコードエディターに組み込まれているため、気軽に利用でき、コーディングの要件に合わせて構成も可能です。

5. スニペットライブラリを採用する

活発なコミュニティを有するフレームワークを使用する利点は、コードの記述に役立つリソースが豊富に存在すること。

スニペットライブラリで開発に頻繁に使用するコードを利用することができ、開発を高速化することができます。

良い例となるのが、ES7+ React/Redux/React-Native snippetsです。構築済みのコードを生成する便利なコマンドが多数用意されています。たとえば、React関数コンポーネントを作成したければ、一切コードを書かずに、「rfce」と入力し、Enterキーを押すだけでOKです。

上のコマンドは、ファイル名に対応した名前の関数コンポーネントを生成します。以下は、ES7+ React/Redux/React-Native snippetsを使用して生成したコード例です。

import React from 'react'
function StudentList() {
  return (
    <div>StudentList</div>
  )
}
export default StudentList

Tailwind CSS IntelliSenseも便利です。Tailwind CSSによって、ウェブページのスタイリングプロセスを簡素化することができます。この拡張には、ユーティリティクラスの提案によるオートコンプリート、シンタックスハイライト、リント機能があります。さらには、コードを書いている最中に色の確認も可能です。

6. CSSとJavaScriptを組み合わせる

大規模なプロジェクトでの作業時、コンポーネントごとに別のスタイルシートファイルを使用すると、ファイル構造が膨れ上がり、管理が難しくなります。

この問題を解決するには、CSSとJSXのコードを組み合わせます。Tailwind CSSやEmotionのようなフレームワークやライブラリを使用します。

以下は、Tailwind CSSを使ったスタイリングの例です。

<p className="font-bold mr-8">resource edge</p>

このコードでは、段落要素に太字のフォントを指定し、右側にマージンを追加しています。実装にはフレームワークのユーティリティクラスを使用します。

以下は、Emotionを使用した要素のスタイリング例です。

<h1
css={css`
  color: black;
  font-size: 30px;
`}
>;
Hello World!
</h1>

7. コンポーネントの作成を制限する

Reactの中核となる機能の1つに、コードの再利用性が挙げられます。コンポーネントを作成すれば、ロジックを書き直すことなく何度でも再利用できます。

とは言え、作成するコンポーネントの数は制限するようにしてください。制限しなければ、不要なファイルでファイル構造が膨れ上がってしまいます。

以下、簡単な例です。

">function UserInfo() {
  return (
    <div>
    <h1>My name is Ihechikara.</h1>
    <;/div>
  );
}
export default UserInfo

上のコンポーネントは、ユーザーの名前を表示します。ユーザーごとにファイルを作ってしまうと、最終的には膨大な数に(上記は簡単なユーザー情報の例だが、実際にはさらに異なる種類のロジックを扱うことになる)。

コンポーネントを再利用可能にするには、以下のようにpropsを利用します。

">function UserInfo({userName}) {
  return (
    <div>
    <h1>My name is {userName}.</h1>
    <;/div>
  );
}
export default UserInfo

その後、このコンポーネントをインポートすれば、何度でも使用することができます。

">import UserInfo from "./UserInfo";
function App() {
  return (
    <div className="App">
    <UserInfo userName={"Ihechikara"} />
    <UserInfo userName={"John"} />
    <UserInfo userName={"Jane"} />
    <;/div>
  );
}
export default App;

これで1つのファイル内に作成された1つのロジックから、3つの異なるUserInfoコンポーネントのインスタンスを作成できます。ユーザーごとにファイルを作成する必要はありません。

8. 遅延読み込みを実装する

Lazy loading is very useful as your React app grows. When you have a big codebase, load time for your web

成長するReactアプリに対して、遅延読み込みは非常に便利です。コード量が増えるにつれ、ウェブページの読み込み時間は遅くなります。これは、すべてのユーザーに対して毎回アプリ全体を読み込むことになるためです。

「遅延読み込み」は、様々な実装で使用されています。ここではJavaScriptとReactについて説明しますが、画像や動画にも遅延読み込みを実装することができます。

Reactは、デフォルトでアプリ全体をバンドルしてデプロイしますが、「コード分割」としても知られる遅延読み込みを使用すれば、この動作を変更できます。

基本的には、特定のある時点でアプリのどの部分を読み込むかを制限します。バンドルを分割し、その場で関連するものだけを読み込むことになります。例えば、最初はサインインのために必要なロジックだけを読み込み、サインインが完了して初めてダッシュボードのロジックを読み込みます。

9. カスタムフックを採用する

フックを使用すると、コンポーネントの状態との通信や、特定の状態変化に関連した処理の実行などが可能になります。毎回クラスコンポーネントを記述する必要はありません。

また、カスタムフックとしてこれを使い回せるようにすることで、使用するファイルごとにその都度ロジックを実装する手間を省けます。カスタムフックを作成すると、アプリの任意の場所にインポート可能です。

以下は、外部APIからデータを取得するフックの例です。

">import { useState, useEffect } from "react";
function useFetchData(url) {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetch(url)
    .then((res) => res.json())
    .then((data) => setData(data))
    .catch((err) => console.log(`Error: ${err}`));
  }, [url]);
  return { data };
}
export default useFetchData;

このフックは、任意のコンポーネントでインポートできるため、外部データを取得するすべてのコンポーネントで、同じロジックを記述する手間が省けます。

Reactで作成できるカスタムフックの種類に制限はありません。どのように使うかは開発者次第です。ただし、複数コンポーネント間で繰り返される機能があれば、再利用可能なカスタムフックを利用することをおすすめします。

10. エラーをログし、管理する

Reactでのエラー処理には、エラー境界、try catch、react-error-boundaryのような外部ライブラリの使用など様々な方法があります。

React 16で導入されたエラー境界は、クラスコンポーネント用の機能のため、今回は触れません。現在はクラスコンポーネントよりも関数コンポーネントの使用が推奨されています。

一方、try catchは命令型コードにのみ有効で、宣言型コードには使用できません。つまり、JSXを扱う際には違う方法を選ぶのが得策です。

react-error-boundaryのようなライブラリを使用することをお勧めします。ライブラリの機能によりコンポーネントをラップでき、Reactアプリがレンダリングされている間にエラーを検出できます。

11. コードを監視し、テストする

開発中にコードをテストすることで、保守性の高いコードを実現できます。しかし残念ながら、多くの開発者がこの作業を怠っています。

ウェブアプリの開発において、テストは重要でないと考える開発者が一部いる一方で、テストの実施には数え切れないメリットがあります。以下はその一部です。

  • エラーやバグの検出が容易になる。
  • バグの検出がコードの質の向上につながる。
  • 単体テストは、データ収集や将来の参照用に文書化できる。
  • バグを早期に発見することで、後々発生した問題の対処にかかる費用を削減できる。
  • バグのないアプリやサイトは信頼性が高まり、大きな成長につながる。

コードのテストには、JestやReact Testing Libraryのようなテストツールを使用できます。他にも多数のテストツールがあるため、お好きなものを選択してください。

またReactアプリを構築する際に、ブラウザ内でアプリを実行してテストすることができます。通常、検出されたエラーは画面に表示されます。これはWordPressサイトの開発にDevKinstaを使用して、ローカルマシン上でWordPressサイトを設計、開発、デプロイするのと似ています。

12. 関数コンポーネントを使用する

Reactでの関数コンポーネントの使用には、様々な利点があります。コード量が少なく、読みやすく、Reactの公式ドキュメントのベータ版も関数コンポーネント(フック)を使用して書き換えられているため、習得しておきたいところです。

関数コンポーネントでは、thisやクラスに頭を悩ませる必要がありません。またフックにより、少ないコード量で簡単にコンポーネントの状態を管理できます。

最近更新されたReactに関するリソースのほとんどで、関数コンポーネントが使用されています。関数コンポーネントに慣れておけば、問題に遭遇しても、コミュニティによる有用な解説やリソースをうまく活かせるはずです。

13. Reactのバージョンアップに追随する

徐々に新たな機能が導入され、古い機能は改善されていくもの。こうした変化に追随するために、公式ドキュメントは常にチェックしておきましょう。

また、SNS上のReactコミュニティに参加すれば最新情報も入手しやすくなります。

Reactのリリース情報を押さえておけば、コードを書き換えてパフォーマンスを最適化するタイミングをうまく見極められるようになります。

同様に、Reactのルーティングに使用されるReact Routerを始めとする、Reactに関連し構築された外部ライブラリの情報収集も大切です。ライブラリの変更内容を知っておけば、それに応じて関係する重要な変更をアプリにも反映でき、プロジェクトをスムーズに推進することができます。

新たなバージョンがリリースされると、一部の機能が非推奨になったり、特定のキーワードが変更されたりすることがあります。このような変更については、常に安全面を考慮し、ドキュメントや解説を参照するようにしてください。

14. 高速で安全なホスティングサービスを利用する

最後に、構築したウェブアプリを一般公開するには、ホスティングサービスを利用してアプリを稼働することになります。このとき、高速で安全なホスティングサービスを選択しましょう。

優れたホスティングサービスには、サイトを簡単に拡張、管理できる様々な機能が付属し、ローカルマシン内のファイルを安全に格納することができます。構築したプロジェクトを世界中に向けて公開するには欠かせないステップです。

ホスティングサービスには、開発者向けの無料で利用できるFirebase、Vercel、Netlify、GitHub Pagesや、有料で提供されているAzure、AWS、GoDaddy、Bluehostなど、様々なものがあります。

あわせて、Kinstaのアプリケーションホスティングサービスもぜひご検討ください。GitHubのリポジトリを接続し、世界25箇所に配置されたKinstaのデータセンターからお好きな場所を選んで、すぐにデプロイ可能です。高速セットアップ、24時間年中無休のサポート、最高レベルのセキュリティ、独自ドメイン、詳細なレポートと監視ツールなどが付帯します。

まとめ

優れたウェブアプリケーションを構築するには、単にReactの使い方を習得するだけでは不十分。AngularやVueなどの他のフレームワークと同様、効率的な開発を実現するためのベストプラクティスがあります。

Reactの流儀に従うことは、アプリ開発に役立つだけでなく、フロントエンド開発者としてのスキルを磨くのにも有益です。効率的でスケーラブル、そして保守しやすいコードを記述するスキルがあれば、その分野の一線で活躍するプロに成長することができます。

Reactでウェブアプリを開発する際には、今回ご紹介したベストプラクティスを踏まえ、利用者が使いやすく、開発者が管理しやすいアプリを目指しましょう。

今回ご紹介した以外にも、Reactのベストプラクティスをご存知ですか?以下のコメント欄でぜひお聞かせください。

Ihechikara Abba

Ihechikara is a software developer and technical writer. He enjoys writing articles on web technologies, programming, and IT-related topics.
Connect with Ihechikara on Twitter.