当サイトは、アフィリエイト広告を利用しています
ReactHooksのパフォーマンス最適化に使われるReact.memoの使い方について
まとめておく。
React.memoを使うことで
をなくすことができ、パフォーマンスを上げることができる。
React.memoのmemoはメモ化とは文字通りメモをするイメージで
コンポーネントの呼び出しで再計算や再レンダリングせず、
キャッシュしている値を返すこと。
そのため2回目以降の呼び出しコストを削減することができる。
React.memoの使い方は下記のようにReact.memo()で囲むだけ。
const Header = props => {return <h1>{props.title}</h1>;};export default React.memo(Header)
exportする時にReact.memoを使用する もしくは
const Header = React.memo(props => {return <h1>{props.title}</h1>;});
コンポーネント作成時に囲む。
React.memoではPropsの値が前回呼び出し時と等価であるかを判定して
を決定している。
また例外として
サンプルを実装してReact.memoの動作を確認する
まずはReact.memoなしで実装し、動かしてみる
/** @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)]}><buttononClick={() => {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;`];
二つのカウンターボタンを押下して
ログを確認すると
count1レンダリングcount2レンダリング↑↑↑↑↑↑↑初回レンダリング↑↑↑↑↑↑↑------------------------------------count1レンダリングcount2レンダリング------------------------------------count1レンダリングcount2レンダリング------------------------------------
カウンターボタンを押下する度
カウント表示コンポーネントが再レンダリングされていることがわかる
実際に動きがみれるようにcodesansboxを載せます
今度はReact.memoありで実装し、動かしてみる
/** @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)]}><buttononClick={() => {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なしの時と同様に二つのカウンターボタンを押下して
ログを確認すると
count1レンダリングcount2レンダリング↑↑↑↑↑↑↑初回レンダリング↑↑↑↑↑↑↑------------------------------------count1レンダリング------------------------------------count2レンダリング------------------------------------
memo化しているので、押されたカウンターボタンの方の
コンポーネントのみが再レンダリングされていることがわかる
実際に動きがみれるようにcodesansboxを載せます
propsにコールバック関数、例えばstateを更新する関数などを
渡した場合はReact.memoしていても再レンダリングされる
/** @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)]}><buttononClick={() => {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;`];
カウンターボタンを押下してログを確認すると
count1レンダリングcount2レンダリング↑↑↑↑↑↑↑初回レンダリング↑↑↑↑↑↑↑------------------------------------count1レンダリングcount2レンダリング------------------------------------count1レンダリングcount2レンダリング------------------------------------
カウンターボタンを押下する度
カウント表示コンポーネントが再レンダリングされていることがわかる
動作確認結果を載せます!
React.memoを使うコンポーネントとしては基本的には
が対象になる。
なのでコンポーネントをすべてメモしていく必要はない。
個人的にはheaderコンポーネントやFooterコンポーネントなどの
一度レンダリングしたらほぼ変わらないコンポーネントなどにも
使います。