当サイトは、アフィリエイト広告を利用しています

【ReactHooks】React.memoでレンダリングを効率化する

作成日:2023月01月08日
更新日:2024年01月28日

React.memoとは?

ReactHooksのパフォーマンス最適化に使われるReact.memoの使い方について
まとめておく。
React.memoを使うことで

  • 不要なコンポーネントの再レンダリング

をなくすことができ、パフォーマンスを上げることができる。

React.memoのmemoとは?

React.memoのmemoはメモ化とは文字通りメモをするイメージで
コンポーネントの呼び出しで再計算や再レンダリングせず、
キャッシュしている値を返すこと。

そのため2回目以降の呼び出しコストを削減することができる。

React.memoの基本構文

React.memoの使い方は下記のようにReact.memo()で囲むだけ。

React.memo_1
const Header = props => {
return <h1>{props.title}</h1>;
};
export default React.memo(Header)

exportする時にReact.memoを使用する もしくは

React.memo_2
const Header = React.memo(props => {
return <h1>{props.title}</h1>;
});

コンポーネント作成時に囲む。

再レンダリング判定

React.memoではPropsの値が前回呼び出し時と等価であるかを判定して

  • メモ化したコンポーネントを使うか
  • 再レンダリングするか

を決定している。
また例外として

  • propsにコールバック関数渡した場合は再レンダリングされる

サンプル実装して動作を確認してみる

サンプルを実装してReact.memoの動作を確認する

React.memoなしの場合

まずはReact.memoなしで実装し、動かしてみる

App.jsx
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
import React, { useEffect, useState } from "react";
//React.memoなし
//カウント表示コンポーネント
const CounterCom = ({ count, com }) => {
console.log(`${com}レンダリング`);
return <div css={[countstyle]}>{count}</div>;
};
export default function App() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
useEffect(() => {
console.log("↑↑↑↑↑↑↑初回レンダリング↑↑↑↑↑↑↑");
}, []);
useEffect(() => {
console.log("------------------------------------");
}, [count1, count2]);
return (
<div>
<h1>React.memo</h1>
<div css={[container(3)]}>
<div css={[countstyle]}>count1</div>
<CounterCom count={count1} com="count1" />
<button onClick={() => setCount1((prev) => prev + 1)}>countUP_1</button>
</div>
<div css={[container(3)]}>
<div css={[countstyle]}>count2</div>
<CounterCom count={count2} com="count2" />
<button onClick={() => setCount2((prev) => prev + 1)}>countUP_2</button>
</div>
<div css={[container(1)]}>
<button
onClick={() => {
setCount1(0);
setCount2(0);
}}
>
clear
</button>
</div>
</div>
);
}
//ここからはEmotionのスタイリング
const container = (grid) => [
css`
display: grid;
grid-template-columns: repeat(${grid}, minmax(50px, 200px));
justify-content: center;
`
];
const countstyle = () => [
css`
text-align: center;
border: 1px solid black;
`
];

二つのカウンターボタンを押下して
ログを確認すると

Log
count1レンダリング
count2レンダリング
↑↑↑↑↑↑↑初回レンダリング↑↑↑↑↑↑↑
------------------------------------
count1レンダリング
count2レンダリング
------------------------------------
count1レンダリング
count2レンダリング
------------------------------------

カウンターボタンを押下する度
カウント表示コンポーネントが再レンダリングされていることがわかる

codesansbox

実際に動きがみれるようにcodesansboxを載せます

React.memo_Sample_notMemo_devbox

React.memoありの場合

今度はReact.memoありで実装し、動かしてみる

App.jsx
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
import React, { useEffect, useState } from "react";
//React.memoあり
//カウント表示コンポーネント
const CounterCom = React.memo(({ count, com }) => {
console.log(`${com}レンダリング`);
return <div css={[countstyle]}>{count}</div>;
});
export default function App() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
useEffect(() => {
console.log("↑↑↑↑↑↑↑初回レンダリング↑↑↑↑↑↑↑");
}, []);
useEffect(() => {
console.log("------------------------------------");
}, [count1, count2]);
return (
<div>
<h1>React.memo</h1>
<div css={[container(3)]}>
<div css={[countstyle]}>count1</div>
<CounterCom count={count1} com="count1" />
<button onClick={() => setCount1((prev) => prev + 1)}>countUP_1</button>
</div>
<div css={[container(3)]}>
<div css={[countstyle]}>count2</div>
<CounterCom count={count2} com="count2" />
<button onClick={() => setCount2((prev) => prev + 1)}>countUP_2</button>
</div>
<div css={[container(1)]}>
<button
onClick={() => {
setCount1(0);
setCount2(0);
}}
>
clear
</button>
</div>
</div>
);
}
//ここからはEmotionのスタイリング
const container = (grid) => [
css`
display: grid;
grid-template-columns: repeat(${grid}, minmax(50px, 200px));
justify-content: center;
`
];
const countstyle = () => [
css`
text-align: center;
border: 1px solid black;
`
];

React.memoなしの時と同様に二つのカウンターボタンを押下して
ログを確認すると

Log
count1レンダリング
count2レンダリング
↑↑↑↑↑↑↑初回レンダリング↑↑↑↑↑↑↑
------------------------------------
count1レンダリング
------------------------------------
count2レンダリング
------------------------------------

memo化しているので、押されたカウンターボタンの方の
コンポーネントのみが再レンダリングされていることがわかる

codesansbox

実際に動きがみれるようにcodesansboxを載せます

React.memo_Sample_Memo_devbox

React.memoあり(propsに関数渡した場合)

propsにコールバック関数、例えばstateを更新する関数などを
渡した場合はReact.memoしていても再レンダリングされる

App.jsx
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
import React, { useEffect, useState } from "react";
//React.memoあり
//カウント表示コンポーネント
const CounterCom = React.memo(({ count, com, cntFunc }) => {
console.log(`${com}レンダリング`);
return (
<>
<div css={[countstyle]}>{count}</div>
<button onClick={() => cntFunc()}>countUP_1</button>
</>
);
});
export default function App() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
//コールバック関数
const handleClick1 = () => {
setCount1((prev) => prev + 1);
};
//コールバック関数
const handleClick2 = () => {
setCount2((prev) => prev + 1);
};
useEffect(() => {
console.log("↑↑↑↑↑↑↑初回レンダリング↑↑↑↑↑↑↑");
}, []);
useEffect(() => {
console.log("------------------------------------");
}, [count1, count2]);
return (
<div>
<h1>React.memo</h1>
<div css={[container(3)]}>
<div css={[countstyle]}>count1</div>
<CounterCom count={count1} com="count1" cntFunc={handleClick1} />
</div>
<div css={[container(3)]}>
<div css={[countstyle]}>count2</div>
<CounterCom count={count2} com="count2" cntFunc={handleClick2} />
</div>
<div css={[container(1)]}>
<button
onClick={() => {
setCount1(0);
setCount2(0);
}}
>
clear
</button>
</div>
</div>
);
}
//ここからはEmotionのスタイリング
const container = (grid) => [
css`
display: grid;
grid-template-columns: repeat(${grid}, minmax(50px, 200px));
justify-content: center;
`
];
const countstyle = () => [
css`
text-align: center;
border: 1px solid black;
`
];

カウンターボタンを押下してログを確認すると

Log
count1レンダリング
count2レンダリング
↑↑↑↑↑↑↑初回レンダリング↑↑↑↑↑↑↑
------------------------------------
count1レンダリング
count2レンダリング
------------------------------------
count1レンダリング
count2レンダリング
------------------------------------

カウンターボタンを押下する度
カウント表示コンポーネントが再レンダリングされていることがわかる

codesandbox

動作確認結果を載せます!

React.memo_Sample_props_func_devbox

React.memoの使いどころ

React.memoを使うコンポーネントとしては基本的には

  • レンダリングコストが高いコンポーネント

が対象になる。
なのでコンポーネントをすべてメモしていく必要はない。

個人的にはheaderコンポーネントやFooterコンポーネントなどの
一度レンダリングしたらほぼ変わらないコンポーネントなどにも
使います。

関連記事

新着記事

タグ別一覧
top