【入門編】 useMemoによるオブジェクト・配列Propsのメモ化 – React実践ガイド

こんにちは!Reactの学習、楽しく進めていますか?
画面上のボタンをクリックしたらフワッと動いたり、入力した文字がリアルタイムで反映されたり……Reactって本当に魔法みたいなフレームワークですよね。

でも、少しずつアプリが大きくなってくると、こんな壁にぶつかったことはありませんか?
「親コンポーネントでちょっと状態が変わっただけなのに、なぜか関係ない子コンポーネントまで全部再レンダリング(再描画)されて、アプリが重い気がする……」

今回は、そんなReactのパフォーマンスの悩みをスッキリ解決してくれる「`useMemo`を使ったオブジェクト・配列Propsのメモ化」について、難しい専門用語をできるだけ排除して、お茶でも飲みながらリラックスして読めるようにお話ししていきますね。

大丈夫、最初は誰もが通る道です。ゆっくり一つずつ紐解いていきましょう!

—

1. なぜオブジェクトや配列を渡すと、子が無駄に怒られちゃうの?(問題の所在)

まずは、イメージしやすいようにおうちの家族に例えてみましょう。

あなた(親コンポーネント)が、お子さん(子コンポーネント)に「今日のお買い物リスト」を渡すとします。

  • ケースA(数値や文字列の場合):

「お小遣いは 1000円 ね」と渡します。親が「やっぱり今日の気分は青い服にしようかな」と自分の洋服を着替えても(=親が再レンダリングしても)、子どもに渡した「1000円」という数字の価値は変わりませんよね。子どもは「おっ、パパ(ママ)は服を着替えたんだな」と思うだけで、リストを新しく書き直す必要はありません。

  • ケースB(オブジェクトや配列の場合):

「今日の買い物リストは `{ apple: 3, banana: 5 }` ね」と、紙に書いて渡したとします。
ここで問題発生! Reactの世界では、親がほんの些細な理由(例えば、画面の隅にある時計の秒数が変わっただけなど)で「よし、今の状態をキープしてもう一回自分を描き直そう(再レンダリング)」したとき、たとえ中身のリンゴとバナナの数がまったく同じであっても、新しくまっさらなメモ用紙に書き直して子どもに渡しちゃうんです。

JavaScriptの世界では、見た目がまったく同じ `{ apple: 3, banana: 5 }` であっても、「新しく作られた別の紙(別のアドレス)」は、過去のものとは「別物」として扱われます。

子どもの立場になってみてください。
親から「はい、さっきとは別の新しい紙に書いた買い物リストだよ!」と渡されたら、「えっ、さっきのリストと中身は一緒だけど、新しい紙だから、念のためにもう一回全部の品物を確認し直さなきゃ……!」と、余計な仕事(=不要な再レンダリング)を始めてしまうんです。これが、Reactでよくある「無駄な再レンダリング地獄」の正体です。

—

2. 救世主「useMemo」の登場! お買い物リストをコピー機でパウチしよう

そこで登場するのが、今回の主役である `useMemo`(ユースメモ) です。

`useMemo` は、一言で言うと「中身が変わっていないなら、前につくったものを大事に取っておいて(キャッシュして)、同じものを使い回すよ!」という魔法の箱です。

先ほどの例で言えば、リンゴの数もバナナの数も変わっていないなら、わざわざ新しい紙に書き直さず、さっきパウチ(ラミネート加工)したお買い物リストをそのまま子どもに渡すイメージですね。

これによって、子コンポーネントは「あ、さっきと同じパウチ済みのリストね。じゃあ中身の確認はパスして休んでよっと!」と、無駄な再レンダリングを回避してパフォーマンスを保つことができるのです。

—

3. 実践!コードで見る `useMemo` の使い方

百聞は一見にしかず。実際のコードを見てみましょう。
今回は、「ユーザー情報(オブジェクト)」を子コンポーネントに渡すシチュエーションを考えてみます。そのままコピペして、お使いの環境で動かしてみてくださいね。

import React, { useState, useMemo } from ‘react’;

// ==========================================
// 子コンポーネント(メモ化の恩恵を受ける側)
// ==========================================
// React.memoで包むことで、「Propsが変わらない限り再レンダリングしない」ようにお願いしています
const UserProfile = React.memo(({ user }) => {
console.log(‘🌟 UserProfile(子)が再レンダリングされました!’);

return (

ユーザープロフィール

名前: {user.name}

年齢: {user.age}歳

);
});

// ==========================================
// 親コンポーネント
// ==========================================
export default function ParentComponent() {
const [count, setCount] = useState(0); // 親のカウンター(これが動くと親が再レンダリングされる)
const [isVip, setIsVip] = useState(false); // ユーザーのステータス(今回は使わないけど例として)

// 【ここがポイント!】
// countが変わるたびに親が再レンダリングされますが、
// useMemoを使うことで、依存している値が変わらない限り、
// このオブジェクト(user)の「実体」を新しく作り直さず、使い回します!
const user = useMemo(() => {
return {
name: ‘山田 太郎’,
age: 28,
};
}, []); // 依存配列が空([])なので、このオブジェクトは最初の一回だけ作られ、ずっと保持されます

return (

useMemoによるPropsメモ化の実験場

{/ 親のステータスを変えるボタン /}

親のカウンター: {count}

{/ 子コンポーネントにオブジェクトのPropsを渡す /}

);
}

コードの解説と心構え

1. `React.memo` との組み合わせがセット!
子コンポーネント側を `React.memo` で包むのが基本セットです。これがないと、親が `useMemo` でオブジェクトを守っていても、子は毎回「やあ!」と呼ばれて再レンダリングされてしまいます(「親が再レンダリングされたら、子は強制的に再描画される」というReactのデフォルトの仕様があるためです)。
2. `useMemo` の第二引数(依存配列 `[]`)
ここに「この値が変わったらオブジェクトを作り直してね」という変数を指定します。今回は名前も年齢も固定なので空っぽ(`[]`)ですが、もしユーザーの年齢が動的に変わるなら `日付未入力` のように指定します。

このコードを動かしてブラウザのコンソール(F12キーを押して確認)を見てみてください。
「親のカウンターを回す」ボタンを何度押しても、`UserProfile(子)が再レンダリングされました!` というログが最初の一回以外流れないはずです!大成功ですね!

—

4. チーフアーキテクトからのアドバイス:なんでもかんでも `useMemo` すればいいってわけじゃない?

さて、ここで少し現場のリアルな開発の裏話もしておきましょう。

「オブジェクトや配列を全部 `useMemo` で包めば、アプリが爆速になって最強なんじゃね?」と思われるかもしれませんが、実はそうでもありません。ここにReact開発の奥深さ(であり、少し厄介なところ)があります。

  • `useMemo` 自体にもコストがかかる

`useMemo` は、「以前の値と今の値を比較する処理」を裏で行っています。小さなデータや、一度きりしか使わないシンプルなオブジェクトに対して毎回 `useMemo` を書いていると、「メモ化のための計算コスト」の方が高くなってしまい、かえってアプリのパフォーマンスを落とす原因になります。

  • いつ使うべきの判断基準
  • 子コンポーネントがめちゃくちゃ重い処理を持っている(グラフを描画するとか、大量のリストを表示するとか)。
  • その子コンポーネントに、オブジェクトや配列のPropsを渡している。
  • 実際に「不要な再レンダリングが原因でアプリがカクついている」とプロファイラ(開発者ツール)で確認できた。

この条件が揃ったときが、`useMemo` の本当の出番です。「なんとなく不安だから全部に貼る」のではなく、「パフォーマンスのボトルネック(おもり)になっている場所をピンポイントで外科手術するように直す」のが、プロのエンジニアの作法なんです。

—

まとめ

いかがでしたでしょうか?

  • Reactでは、オブジェクトや配列のPropsは、中身が同じでも再レンダリングのたびに「別物の紙」として新しく作られてしまう。
  • それが原因で、子コンポーネントが不要な再レンダリングを起こしてしまう。
  • そこを救うのが `useMemo`。`React.memo` とセットで使うことで、オブジェクトを大切にキャッシュして使い回せる。
  • ただし、何でもかんでも貼ればいいわけではない(適材適所が大事!)。

最初は難しく感じるかもしれませんが、実際に手を動かして、ブラウザのコンソールで「おっ、今回は子が無駄に動いてないぞ!」と確認できたときは、なかなかの達成感がありますよ。

あなたのReactライフが、少しでも快適で楽しいものになりますように。
わからないところでつまずいたら、いつでもまたこの記事に帰ってきてくださいね。応援しています!

コメント

タイトルとURLをコピーしました