React FCを活用して、機能的かつ読みやすいコードを書こう
Reactにはコンポーネントを定義する方法がいくつかあります。 その中でも関数を利用して作るものが Functional Component (FC)です。
以前のReactではクラスを使ったコンポーネントが主流でしたが、FCを使うことでコード量を抑えやすくなります。 さらに構造がシンプルになることもあり、多くのプロジェクトでFCが一般的に使われるようになっています。
実務でもFCを導入するメリットは多いです。 読みやすさの向上や開発時のメンテナンス効率などが挙げられます。 これからReactを学んでいく皆さんにとっても、FCは理解しやすい選択肢ではないでしょうか。
ただしReact FCと一口に言っても、実務ではさまざまな書き方やパターンがあります。 そこで最初に、基本的なコード例を見ながらFCの概要を押さえておきましょう。
シンプルなコード例
Reactの最新バージョンを前提にした、最もシンプルなFC例を示します。
import React from "react"; function HelloMessage() { return ( <div> <p>こんにちは、React FCの世界へようこそ。</p> </div> ); } export default HelloMessage;
このように、JavaScriptの関数をそのまま利用して、return
で描画内容を返す形です。
このコードでは、HTMLタグのようなJSX記法を返しています。
JSXがどのように扱われるかに慣れていない場合でも、直感的にUIをイメージしやすいかもしれません。
クラスコンポーネントとの違い
ReactではクラスコンポーネントとFCが共存しています。 しかし、最近のプロジェクトではFCを使う場面が多いです。 次に、両者の違いをざっくりと把握しておきましょう。
ライフサイクルメソッドがない
クラスコンポーネントにはcomponentDidMount
やcomponentDidUpdate
などのライフサイクルメソッドがあります。
これらはコンポーネントの状態変化に応じて呼び出される仕組みです。
一方でFCにはそれらが直接は存在しません。 でも、React HooksのuseEffectを利用することで、クラスコンポーネントのライフサイクルメソッドと同等のことが可能です。
thisキーワードを使わない
クラスコンポーネントではthis
を使ってステートやメソッドにアクセスします。
しかしFCでは、this
を使う必要がありません。
その結果、コードの見通しがシンプルになることが多いです。
stateの管理方法
クラスコンポーネントではthis.state
とthis.setState()
を使い、ステートを管理します。
しかしFCではReact HooksのuseStateを使います。
これにより、関数の中でステートを定義して更新できるようになります。
クラスコンポーネントと比べると、FCのほうが関数らしい書き方に統一されるため、初めて触れる皆さんでも学びやすいかもしれません。 ただし、クラスコンポーネントからFCへの移行時にコードの構造が変わる点には注意しましょう。
React HooksとFCの関係
FCの普及に大きく貢献したのがReact Hooksです。 Hooksを活用すれば、クラスコンポーネントでしか実現できなかった機能がFCでも簡単に扱えるようになります。
React Hooksの基本
ReactにはさまざまなHooksが用意されています。 その中でもよく使われるものを、初心者の方にもわかりやすく説明します。
useState
コンポーネントで値を保存・更新するためのフックです。 たとえば、フォームの入力値やカウンターの数値など、画面上で変化する値を管理できます。
// 例:カウンターの値を管理する const [count, setCount] = useState(0); // countが現在の値、setCountが更新用の関数 // 0は初期値
useEffect
コンポーネントの外部と連携する処理を扱うためのフックです。 APIからデータを取得したり、タイマーを設定したり、画面の表示後に何か処理を実行したい場合に使います。
// 例:コンポーネントが表示されたときにデータを取得する useEffect(() => { // ここに実行したい処理を書く fetchData(); }, []); // 空の配列を渡すと、初回表示時のみ実行
useContext
親コンポーネントから遠く離れた子コンポーネントにデータを直接渡すためのフックです。 たとえば、ユーザーの設定やテーマカラーなど、アプリ全体で共有したい値を管理できます。
// 例:テーマの設定を取得する const theme = useContext(ThemeContext); // ThemeContextから値を取得して使える
useMemo
計算コストが高い処理の結果を記憶しておくためのフックです。 たとえば、大量のデータをフィルタリングする場合など、毎回同じ計算をしなくて済むようになります。
// 例:フィルタリング結果を記憶する const filteredItems = useMemo(() => { return items.filter(item => item.price > 1000); }, [items]); // itemsが変更されたときのみ再計算
useCallback
関数を記憶しておくためのフックです。 子コンポーネントに渡す関数が、レンダリングのたびに新しく作られるのを防ぎます。 これにより、不要な再レンダリングを減らすことができます。
// 例:ボタンクリック時の処理を記憶する const handleClick = useCallback(() => { console.log('クリックされました'); }, []); // 依存する値がないので、常に同じ関数を使い回す
これらのHooksは、それぞれ特定の目的のために設計されています。
最初はuseState
とuseEffect
から使い始めて、徐々に他のHooksも使っていくのがおすすめです。
useStateの利用例
実際にFCでステート管理をするなら、useStateをよく使います。 簡単なカウンターコンポーネントを例として示します。
import React, { useState } from "react"; function Counter() { const [count, setCount] = useState(0); const handleClick = () => { setCount((prevCount) => prevCount + 1); }; return ( <div> <p>カウント: {count}</p> <button onClick={handleClick}>増やす</button> </div> ); } export default Counter;
ここでは、useState(0)
と書くことで、初期値を0に設定しています。
count
が現在の値、setCount
が更新用の関数です。
この2つはセットで使用されます。
クラスコンポーネントに比べると、シンプルなコードになっているのがわかるのではないでしょうか。
実務におけるReact FCの活用シーン
React FCは実務でも幅広く使われます。 特に、次のような状況で役立つことが多いです。
小規模コンポーネントの量産
Webアプリケーションでは、小さなコンポーネントをたくさん作って組み合わせることがよくあります。 FCはコンパクトに書きやすいので、部品が増えてもコードをスッキリ保ちやすいです。
たとえば、ヘッダーやフッター、ボタンなどのUIパーツを量産する場合に適しています。 複雑なクラス定義をしなくても済むので、他のメンバーがレビューするときにも分かりやすいと思います。
ステートフルなロジックを分割する
React Hooksと組み合わせることで、ステートフルなロジックを複数の小さな関数に分割するアプローチがとりやすくなります。 たとえば、フォーム入力を管理するロジックや、API通信のロジックなどを別々のHooksに切り出せます。
チームで開発するときには、ロジックを切り分けておくと保守が楽になる場合があります。 それをFCベースで書いておくと、関数同士の依存関係が明確になりやすいです。
パフォーマンス向上を狙う
Reactは仮想DOMのおかげで高速に動作しますが、再レンダリング回数が増えすぎるとパフォーマンスに影響が出る場合があります。
FCではuseMemo
やuseCallback
などで必要な部分だけをメモ化できます。
これにより、パフォーマンス最適化がやりやすいのです。
ただし、やみくもにメモ化するよりも、どこがボトルネックかを見極めることが大切です。 実務でも、パフォーマンスを測定しながら部分的に最適化するケースが多いでしょう。
Propsの扱い方
React FCでは、コンポーネントにpropsを渡して動的に表示内容を切り替えることができます。 これはクラスコンポーネントと仕組み自体は変わりません。 ただし、FCの方がコードが短くなる場合が多いです。
基本的なpropsの受け取り方
次の例では、メッセージをpropsとして受け取り、表示するだけのFCを示します。
import React from "react"; function Greeting({ message }) { return ( <div> <p>{message}</p> </div> ); } export default Greeting;
呼び出す側で下記のように指定すると、message
に文字列が渡されます。
<Greeting message="React FCは便利ですね" />
関数の引数としてオブジェクトを受け取る形なので、分割代入を使うとスッキリ書けます。 もし複数のpropsを受け取るなら、コンマ区切りで並べればOKです。
propsの型チェック
コードが大きくなると、受け渡すpropsが複雑になることがあります。
TypeScriptを使うと型安全に管理できますが、JavaScriptだけで進めるならprop-types
ライブラリでチェックする方法もあります。
ただし最新の実務では、TypeScriptとReactを組み合わせるプロジェクトも珍しくありません。 いずれにしても、propsの構造をしっかり把握しておくことが重要です。
複数のFCを連携させる
Reactのアプリケーションは小さなコンポーネントが集まってできています。 つまり、FCを組み合わせて大きな機能を作り上げていくのが基本的な流れです。
親子関係とデータフロー
Reactでは、データの流れが基本的に上から下へ向かいます。 親コンポーネントでステートを持ち、子コンポーネントへpropsを渡す構造です。 下記の例は、親コンポーネントでカウンターの値を管理し、子コンポーネントに表示部分だけを任せるイメージです。
import React, { useState } from "react"; function CounterDisplay({ count }) { return <p>現在のカウント: {count}</p>; } function ParentComponent() { const [count, setCount] = useState(0); const increment = () => setCount((prev) => prev + 1); return ( <div> <CounterDisplay count={count} /> <button onClick={increment}>カウントを増やす</button> </div> ); } export default ParentComponent;
このように、FC同士を組み合わせれば機能を段階的に拡張できます。 複数の箇所でステートを使う場合は、どの階層でステートを持たせるかを検討すると良いでしょう。
コンテキストAPIでグローバルな状態管理
親子関係が深くなると、propsを渡すためのコードが増えがちです。 そこでReactのコンテキストAPIを使うと、深い階層のコンポーネントにも直接データを渡しやすくなります。
FCでコンテキストAPIを扱う場合はuseContextが便利です。 プロバイダを用意して、その中で使いたいデータを宣言し、下層のコンポーネントから利用します。 規模が大きいアプリケーションでは、Reduxなどの状態管理ライブラリを併用することもあります。
React FCにおけるイベントハンドリング
ボタンをクリックしたり、フォームに入力したりするイベント処理は、ユーザーとアプリをつなぐ重要な仕組みです。 FCの場合は、関数を定義し、要素の属性に割り当てるだけで実行できます。
イベントハンドラの定義
次のようにクリックイベントを定義する例を見てみます。
import React from "react"; function ClickExample() { const handleClick = () => { console.log("クリックされました"); }; return ( <button onClick={handleClick}> クリックしてください </button> ); } export default ClickExample;
このhandleClick
がイベントハンドラとして機能します。
クラスコンポーネントでは、コンストラクタでバインドを行う必要があったりしましたが、FCではその手間がありません。
関数を定義してonClick
に割り当てるだけです。
Formイベントの取り扱い
フォーム送信などのイベント処理もFCで容易に扱えます。 たとえば、以下のようにフォーム送信をハンドリングする例があります。
import React, { useState } from "react"; function SimpleForm() { const [inputValue, setInputValue] = useState(""); const handleSubmit = (e) => { e.preventDefault(); console.log("送信された値:", inputValue); }; return ( <form onSubmit={handleSubmit}> <input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} /> <button type="submit">送信</button> </form> ); } export default SimpleForm;
送信時にページのリロードを避けたい場合はe.preventDefault()
を呼ぶのが一般的です。
FCであっても同じ考え方で、スムーズにイベントを処理できます。
よく使われるデザインパターンやベストプラクティス
実務でReact FCを使うときには、一定のパターンを覚えておくと役に立ちます。 ここでは代表的なものを挙げてみます。
プレゼンテーショナルコンポーネントとコンテナコンポーネント
プレゼンテーショナルコンポーネント
見た目やレイアウトに関する処理だけを担うコンポーネントです。 ステートを持たないか、持っても最小限にとどめる場合が多いです。
コンテナコンポーネント
データの取得やビジネスロジックなどを担当します。 ステートを持っており、プレゼンテーショナルコンポーネントに必要な情報を渡します。
こうして役割を分けると、見た目とロジックが明確に分離されるので、保守性が向上しやすいです。
カスタムフックの活用
複数のコンポーネントで似たようなロジックが必要になった場合は、カスタムフックとして切り出すのが便利です。 たとえば、フォーム入力やAPIデータ取得などが繰り返し現れるなら、共通処理をカスタムフックにまとめることを検討できます。
カスタムフックはuse
から始まる名前にするというルールがあります。
関数の中でHooksを使えば、他のFCでも再利用できる形になります。
メモ化で不要な再レンダリングを減らす
React.memo
FCをラップし、propsに変化がなければ再レンダリングをスキップできる仕組みです。
useCallbackとuseMemo
関数や値をメモ化して、依存する変数に変化がないときは再生成しません。
多くのコンポーネントが連携しているアプリケーションでは、ちょっとした変更でも多数の再レンダリングが発生する場合があります。 そのような状況で、これらの機能を使ってパフォーマンスを保つことがよくあります。
実務でありがちな落とし穴
FCは便利な反面、気をつけないと思わぬバグやパフォーマンス問題を引き起こすことがあります。
無駄な再レンダリング
FCはステートやpropsが更新されると、該当コンポーネントが再描画されます。 子コンポーネントに渡す関数が毎回新しく生成されると、子コンポーネントまで再レンダリングが伝播することがあります。 そこで、useCallbackやReact.memoを適切に使わないと、予想外に描画コストが上がることがあるのです。
依存配列の指定ミス
useEffect
やuseCallback
など、Hooksには依存配列を指定します。
ここに必要な変数を入れ忘れると、期待したタイミングで処理が動かなくなったり、逆に無限ループが発生したりする場合があります。
依存配列を正しく管理するのは重要です。
ステートの更新ロジック
useState
の更新では、常に最新のステート値を参照しようとして、誤って古い値に基づく計算をしてしまうケースがあります。
たとえばsetCount(count + 1)
と書くのではなく、setCount((prevCount) => prevCount + 1)
のように前の値をコールバック関数の中で扱う方法を意識すると良いでしょう。
サンプルコードで学ぶ流れ
ここまでFCの概要やメリット、気をつけるポイントを見てきました。 では、少し規模の大きなサンプルを示して、実務的な流れをイメージしてみます。
コンポーネント構成例
App
ルートとなるコンポーネントです。 全体のレイアウトを管理します。
Header
画面上部のヘッダーを表示します。
TodoList
ToDoアイテムのリストと、追加フォームを管理します。 ステートを持ち、各アイテムを表示する子コンポーネントにデータを渡します。
TodoItem
1つのToDoアイテムを表示します。 完了チェックや削除ボタンなどの操作を受け取ります。
こうした分割を行い、それぞれをFCとして定義します。
コード例
import React, { useState } from "react"; function TodoItem({ item, onToggle, onDelete }) { return ( <li> <input type="checkbox" checked={item.completed} onChange={() => onToggle(item.id)} /> <span style={{ textDecoration: item.completed ? "line-through" : "none" }}> {item.title} </span> <button onClick={() => onDelete(item.id)}>削除</button> </li> ); } function TodoList() { const [todos, setTodos] = useState([]); const [inputTitle, setInputTitle] = useState(""); const handleAdd = () => { if (!inputTitle.trim()) return; const newTodo = { id: Date.now(), title: inputTitle, completed: false, }; setTodos((prev) => [...prev, newTodo]); setInputTitle(""); }; const handleToggle = (id) => { setTodos((prev) => prev.map((todo) => todo.id === id ? { ...todo, completed: !todo.completed } : todo ) ); }; const handleDelete = (id) => { setTodos((prev) => prev.filter((todo) => todo.id !== id)); }; return ( <div> <h2>ToDoリスト</h2> <div> <input type="text" value={inputTitle} onChange={(e) => setInputTitle(e.target.value)} /> <button onClick={handleAdd}>追加</button> </div> <ul> {todos.map((item) => ( <TodoItem key={item.id} item={item} onToggle={handleToggle} onDelete={handleDelete} /> ))} </ul> </div> ); } function Header() { return ( <header> <h1>React FCで作るToDoアプリ</h1> </header> ); } function App() { return ( <div> <Header /> <TodoList /> </div> ); } export default App;
ここではAppが親のコンポーネントとして、Header
とTodoList
を表示しています。
TodoList
がToDoのステートを持ち、TodoItem
に対してpropsを渡す仕組みです。
こうした構成により、アプリの機能を小さなFCに分割しながら実装できます。
React FCを活かすテクニックまとめ
React FCは柔軟な仕組みです。 しかし効果的に使うためには、いくつかのテクニックを押さえておくと良いです。
ステート管理ライブラリとの連携
アプリ規模が大きくなると、ステート管理を一元化するためにReduxやRecoilなどのライブラリを導入することがあります。 FCであっても問題なく連携可能です。 Hooks専用の書き方を採用しているライブラリも増えているため、FCとの相性は悪くありません。
非同期処理
データの取得や送信など、実務では非同期処理が必要になることが多いです。
React FCでは、useEffect
を利用してAPI通信を行うケースが代表的です。
通信が完了したタイミングでステートを更新し、再レンダリングでUIを更新します。
コンパイル時の最適化
TypeScriptの導入やBabelの最適化設定、ESLintを使ったコードチェックなど、開発ツールと組み合わせるとさらに生産性が高まります。 FCはシンタックス的にもシンプルなので、自動整形ツールの恩恵を受けやすいです。
チーム開発への応用
Reactプロジェクトはチームで行うことが多いです。 FCの設計指針を統一することで、チーム内の連携がスムーズになります。
コーディング規約
- 変数名や関数名の付け方を統一する
- ファイル構成を統一し、コンポーネントごとにフォルダを分ける
- 1ファイルに複数のコンポーネントを定義しすぎない
こうしたルールを決めておけば、誰が書いても同じようなスタイルになり、保守性が高まります。
Pull Requestでのレビュー
チーム開発ではPull Requestでレビューをすることが一般的です。 FCを使っているとコードが短くなりやすいので、レビューもしやすいのではないでしょうか。 複雑なロジックをカスタムフックなどに分割しておけば、どこを重点的に確認すべきかがわかりやすくなります。
テスト戦略
JestやReact Testing Libraryなどを使ったテストも、FCでの記述と相性がいいです。 関数単位でテストを組み立てやすいので、ユニットテストやスナップショットテストを導入しやすいでしょう。 ただし、本格的なテストを組むには依存モジュールやモック化などの知識が必要です。
保守やアップデートのポイント
Reactはバージョンアップが活発なフレームワークです。 最新機能が追加された場合、FCが拡張されることもあります。
Reactのバージョンチェック
新しいReactではHooksがより使いやすくなることや、新しい勧告が出ることがあります。 FC関連のAPI変更にすぐ対応できるよう、バージョンを定期的にチェックしておくのが良いでしょう。 また、クラスコンポーネントが非推奨になるという話は現時点ではありませんが、公式ドキュメントでもFCを使う例が多く紹介されています。
ライブラリとの互換性
UIコンポーネント系のライブラリやルーティングライブラリを利用する際にも、FCで問題なく使えるケースがほとんどです。 ただしライブラリ側がクラスコンポーネントを前提としている場合、独自の対応が必要になることがあります。 そういったケースがあるかどうか、事前にドキュメントをチェックすると安心です。
将来の拡張に備える
開発初期はシンプルだったFCでも、あとから機能が増えて複雑化する可能性があります。 カスタムフックやコンポーネント分割を適切に行っておけば、大規模化しても保守しやすい構造を維持できます。 逆に1つのコンポーネントに機能を詰め込みすぎると、後々リファクタリングが必要になるかもしれません。
まとめと次のステップ
React FCは、初心者でも理解しやすい記述スタイルと、柔軟な拡張性を兼ね備えたアプローチだと言えます。 クラスコンポーネントと比べても、コードが短くなりやすく、メンテナンスもスムーズに進みやすいのではないでしょうか。
実務でFCを使いこなすには、Hooksの知識は欠かせません。
特にuseState
やuseEffect
などの基本的なHooksをどのように活用するかを理解しておくと、開発の幅が広がります。
さらに、パフォーマンス最適化の手法や、チームでのコーディング規約も含めて把握しておくと、より実践的にFCを取り入れられます。
皆さんがReactで開発を行う際、FCを積極的に採用していくと、今後のアップデートにも柔軟に対応できるかもしれません。 機能追加やバグ修正のたびに、コードが長くて困るという状況になりにくい点もFCの魅力です。
React FCを使い始めると、クラスコンポーネントには戻れないと感じる方もいるかもしれません。 それだけ記述がシンプルで、一度慣れると直感的な開発ができると言われています。
FCはReact開発において、中核を成すスタイルの一つです。 クラスコンポーネントが不要になるわけではありませんが、これからのReactで中心的な役割を果たす可能性が高いでしょう。
新たなプロジェクトを立ち上げるときや、既存のコードをリファクタリングするときに、ぜひFCの選択肢を検討してみてください。 チーム全体でコードの読みやすさを追求でき、保守や機能拡張にも適応しやすい開発体験が望めるはずです。