React関数コンポーネントの本質:ただの「UIを返す関数」と侮るなかれ
現場でコードレビューをしていると、Reactコンポーネントを「とりあえず動くから」という理由で、なんとなく書き連ねている中級エンジニアによく出会います。しかし、Reactの本質は「Propsという入力を受け取り、UI(React要素)という出力を返す純粋関数」であるという一点に集約されます。
ここを腹落ちさせているかいないかで、数ヶ月後のコードのメンテナンス性は天と地ほどの差が出ます。今回は、関数コンポーネントの定義を「ただのJavaScript」として捉え直し、ブラウザの裏側で何が起きているのかという視点から深掘りしていきましょう。
—
関数コンポーネントの「正体」を解剖する
Reactのコンポーネントは、魔法でもなんでもありません。ただのJavaScriptの関数です。
/
- 現場でよく見る「綺麗な」コンポーネントの基本型
- 引数でpropsを受け取り、React要素(JSX)を返すだけのシンプルな関数
/
const UserProfile = ({ name, age, isActive }) => {
// ここで不純な処理(副作用)を書いてはいけないのが鉄則
// 外部APIを叩いたり、DOMを直接触ったりするのはuseEffectの仕事
return (
{name}
年齢: {age}
);
};
このコードを見たとき、多くの人は「JSXを書いている」と認識しますが、コンパイル後のブラウザはこう見ています。
// JSXはビルドプロセスでReact.createElementに変換される
const UserProfile = (props) => {
return React.createElement(
‘div’,
{ className: props.isActive ? ‘active’ : ” },
React.createElement(‘h2’, null, props.name),
React.createElement(‘p’, null, `年齢: ${props.age}`)
);
};
この変換を知っていると、「なぜコンポーネント名はパスカルケース(大文字始まり)でなければならないのか?」という疑問も一瞬で解けます。`React.createElement(‘div’)`なら文字列でいいですが、`React.createElement(UserProfile)`と呼ぶには、JS側で変数として参照できる必要があります。HTMLのタグと区別するために、Reactは「大文字ならコンポーネント、小文字ならHTMLタグ」と解釈しているわけです。
—
実務で差がつく「関数の純粋性」とコンポーネント設計
中級者が陥りやすい罠が、コンポーネント関数の直下に「不必要なロジック」を詰め込んでしまうこと。
コンポーネントは、親コンポーネントが再レンダリングされるたびに、最初から最後まで実行され直すという性質があります。つまり、関数内部に重い計算処理をベタ書きすると、UIの描画のたびにCPUを無駄遣いすることになるのです。
現場で使えるベストプラクティス:ロジックの分離
// 悪い例: 毎回実行されてしまう計算処理
const UserList = ({ users }) => {
const sortedUsers = users.sort((a, b) => b.score – a.score); // 毎回ソートが走る
return
;
};
// 良い例: useMemoで計算をメモ化するか、関数の外に追い出す
import { useMemo } from ‘react’;
const UserList = ({ users }) => {
// 依存配列に変化がない限り、前回の結果を再利用する
const sortedUsers = useMemo(() => {
return […users].sort((a, b) => b.score – a.score);
}, [users]);
return
;
};
—
なぜ「仮想DOM」を意識すべきなのか
コンポーネントが返すのは「実際のDOM」ではなく、「仮想DOMツリー(React要素のオブジェクト)」です。
Reactは、関数が返すこのオブジェクトの塊(Object Tree)を前回のレンダリング結果と比較(Diffing)し、変更があった最小限の箇所だけをブラウザのDOMに反映させます。これが「Reactは高速だ」と言われる所以ですが、裏を返せば「不要なコンポーネントの再レンダリングは、この比較処理を無駄に発生させる」ということです。
プロのTips:
「コンポーネントは小さく分割しろ」とよく言われますが、これは「再レンダリングの範囲を最小化するため」です。親コンポーネントが肥大化すると、末端のボタン一つを押しただけで、関係ないパーツまで再計算が走ります。
- コンポーネントの境界線を意識する: 「ここから先はデータが更新されたときだけ再描画されればいい」という単位で切り出してください。
- Propsは必要最小限に: オブジェクト全体を渡すのではなく、必要な値だけを渡す。これで`React.memo`による最適化も効きやすくなります。
—
最後に:関数コンポーネントは「UIの設計図」である
関数コンポーネントを書くときは、単に「タグを返す場所」ではなく、「このコンポーネントはどのデータに反応して、どう変わるべきか」という状態の依存関係を意識してみてください。
「コードが動くこと」はゴールではなくスタートラインです。
「誰が読んでも意図が伝わり、かつReactの再レンダリングの仕組みに抗わないコード」を書くこと。それがシニアエンジニアへの第一歩です。
まずは、今日書いているコンポーネントを一度眺めてみてください。「これ、本当にこの関数の中でやる必要がある計算か?」と自問自答するだけで、あなたのコードの質は劇的に向上しますよ。

コメント