今回は、Reactのパフォーマンスを悪くする再レンダリングについてです。
▼この記事で理解できる内容は以下になります。
- React.memoの使い方
- React.memoを使う場合、使わない場合の比較
この記事は、React初心者の方、React.memoについて知りたい方を対象としています。
React.memoってどんな機能?
React.memoはどのようにパフォーマンスアップさせるのかReact公式の内容を見ると参考になります。
React.memoは使い道
頻繁に再レンダリングされるコンポーネントで、書き換える必要のない子コンポーネントがぶら下がっている場合に利用するとパフォーマンスアップにつながる。
React.memoは最後のレンダリングを記憶している
もしあるコンポーネントが同じ props を与えられたときに同じ結果をレンダーするなら、結果を記憶してパフォーマンスを向上させるためにそれを React.memo でラップすることができます。つまり、React はコンポーネントのレンダーをスキップし、最後のレンダー結果を再利用します。
引用元: React公式
前回レンダーされた結果を記憶しているため、同じ内容であれば再レンダリングを行わないためパフォーマンスアップにつながる。
propsの変更のみチェックしている
React.memo は props の変更のみをチェックします。
引用元: React公式
子のコンポーネントでReact.memoが使われている場合、propsの変更のみチェックしているため、変更がない場合は再レンダリングを行わないためパフォーマンスアップにつながる。
React.memoの使い方
React.memoはラップするだけで簡単に利用することができます。
React.memoの基本形
const SampleMemo = React.memo(props => { return <p>{props.memo}</p> });
React.memoの使用例
親コンポーネントにある2つのボタンをクリックし、React.memoを使っている子コンポーネントと使っていない子コンポーネントのレンダリングの動作を確認してみます。
Vue.jsではcomputedが、それに近い動きをします。computed内にあるプロパティに変化がない場合は再レンダリングが行われません。
以下のようなサンプル画面を準備しました。
▼サンプルの画面
親コンポーネントに2つのボタンとボタンクリックした時にカウントアップされる値をpropsで受け取る子コンポーネントです。
子コンポーネントは、片方の「NormalCountコンポーネント」は特に何もしていない子コンポーネント。
「MemoCountコンポーネント」は、React.memoを利用した子コンポーネントです。それぞれTypeScriptで書いています。
親コンポーネント
import React, { useState } from 'react' import NormalCount from './components/NormalCount' import MemoCount from './components/MemoCount' export default function App() { const [normalCount, setNormalCount] = useState(0) const [memoCount, setMemoCount] = useState(0) return ( <div> <NormalCount normalCount={normalCount}/> <MemoCount memoCount={memoCount}/> <button onClick={() => setNormalCount(normalCount + 1)}>NORMAL</button> <button onClick={() => setMemoCount(memoCount + 1)}>MEMO</button> </div> ) }
NormalCountコンポーネント
import React from 'react' interface NormalProps { normalCount: number } const NormalCount: React.FC<NormalProps> = (({ normalCount }) => { console.log('■ Normal Count') return ( <div> <p>normalCount: {normalCount}</p> </div> ) }) export default NormalCount
MemoCountコンポーネント
import React from 'react' interface MemoProps { memoCount: number } const MemoCount: React.FC<MemoProps> = React.memo(({ memoCount }) => { console.log('▼ Memo Count') return ( <div> <p>memoCount: {memoCount}</p> </div> ) }) export default MemoCount
React.memoの使用した結果
「MEMOのボタン」を押下した場合は、NormalCountコンポーネントの方の値に影響がなくても再レンダリングされています。
逆に「NORMALのボタン」を押下した場合は、React.memoでラップされたMemoCountコンポーネントのpropsの値に影響がないため再レンダリングされていないのがわかります。
React.memoの使い方まとめ
今回のように軽いコンポーネントの場合は、パフォーマンスに影響がでることはないと思いますが、レンダリングに時間がかかるコンポーネントなどで毎回再レンダリングされると、パフォーマンスに影響します。
頻繁に値が変化するようなアプリの場合は、React.memoの利用も考えながらコンポーネントを作っていきたいですね。