✨
【React】レンダリングの最適化(memo,useCallback,useMemo)
レンダリング最適化
使い分け
memo | useCallback | useMemo | |
---|---|---|---|
対象 | コンポーネント | 関数 | 変数 |
memo
コード(最適化前)
import {useState} = "react"
const Parent = () => {
const [count, setCount] = useState(0);
consol.log("親コンポーネントが呼ばれました");
return(
<div>
<div>{count}</div>
<button onClick={onClick}>Click</button>
<ChildComponent />
</div>
);
}
const ChildComponent = () => {
console.log("子コンポーネントが呼ばれました");
return(
<div>This is ChildComponent</div>
);
}
出力結果
親コンポーネント中のボタン要素を押しても、子コンポーネントは変わっていないのに、親コンポーネントの再レンダリングに合わせて子コンポーネントも再レンダリングされてしまう。
//レンダリング時
親コンポーネントが呼ばれました
子コンポーネントが呼ばれました
//親ボタンclick時
親コンポーネントが呼ばれました
子コンポーネントが呼ばれました //再レンダリング不必要
コード(最適化後)
import {useState, memo} = "react"
const Parent = () => {
const [count, setCount] = useState(0);
consol.log("親コンポーネントが呼ばれました");
return(
<div>
<div>{count}</div>
<button onClick={onClick}>Click</button>
<ChildComponent />
</div>
);
}
const ChildComponent = memo(() => {
console.log("子コンポーネントが呼ばれました");
return(
<div>This is ChildComponent</div>
);
})
出力結果
親ボタンが押されたときに再レンダリングする必要のない子コンポーネントを再レンダリングしないようになった。
//レンダリング時
親コンポーネントが呼ばれました
子コンポーネントが呼ばれました
//親ボタンclick時
親コンポーネントが呼ばれました
memoの使うタイミング
- 静的な子コンポーネント
- propsが変数だけの子コンポーネント
useCallback
コード(最適化前)
import {useState, memo} = "react"
const Parent = () => {
const [count, setCount] = useState(0);
const [childCount, setChildCount] = useState(0);
consol.log("親コンポーネントが呼ばれました");
const onClick = () => {
setCount(count + 1);
}
const onClildClick = () => {
setChildCount(childCount + 1);
}
return(
<div>
<div>{count}</div>
<button onClick={onClick}>Click</button>
<ChildComponent onClick={onClildClick}/>
</div>
)
}
const ChildComponent = memo((props) => {
console.log("子コンポーネントが呼ばれました")
return(
<div>
<div>This is ChildComponent</div>
<button onClick={props.onChildClick}>Click!<button>
</div>
)
})
出力結果
子コンポーネントにpropsで関数が渡されると"新しい関数を受け取った"ように振る舞って、親コンポーネントが再レンダリングされると、子コンポーネントに変化がなくても再レンダリングされてしまう。
//レンダリング時
親コンポーネントが呼ばれました
子コンポーネントが呼ばれました
//親ボタンclick時
親コンポーネントが呼ばれました
子コンポーネントが呼ばれました //再レンダリング不必要
コード(最適化後)
import {useState, memo, useCallback} = "react"
const Parent = () => {
const [count, setCount] = useState(0);
const [childCount, setChildCount] = useState(0);
consol.log("親コンポーネントが呼ばれました");
const onClick = () => {
setCount(count + 1);
}
const onClildClick = useCallback(() => {
setChildCount(childCount + 1);
},[childCount])
return(
<div>
<div>{count}</div>
<button onClick={onClick}>Click</button>
<ChildComponent onClick={onClildClick}/>
</div>
)
}
const ChildComponent = memo((props) => {
console.log("子コンポーネントが呼ばれました")
return(
<div>
<div>This is ChildComponent</div>
<button onClick={props.onChildClick}>Click!<button>
</div>
)
})
出力結果
//レンダリング時
親コンポーネントが呼ばれました
子コンポーネントが呼ばれました
//親ボタンclick時
親コンポーネントが呼ばれました
useCallbackの使うタイミング
- propsに関数がある子コンポーネント
useMemo
コード(最適化前)
import {useState, memo, useCallback} = "react"
const Parent = () => {
const [count, setCount] = useState(0);
const [childCount, setChildCount] = useState(0);
consol.log("親コンポーネントが呼ばれました");
const onClick = () => {
setCount(count + 1);
}
const squar = (count) => {
console.log("2乗する関数が実行されました")
return count * count
}
const squarCount = sqrar(count);
const onClildClick = useCallback(() => {
setChildCount(childCount + 1);
},[childCount])
return(
<div>
<div>{count}</div>
<div>{squarCount}</div>
<button onClick={onClick}>Click</button>
<ChildComponent onClick={onClildClick}/>
<div>{childCount}</div>
</div>
)
}
const ChildComponent = memo((props) => {
console.log("子コンポーネントが呼ばれました")
return(
<div>
<div>This is ChildComponent</div>
<button onClick={props.onChildClick}>Click!<button>
</div>
)
})
出力結果
子コンポーネントのボタンを押したときに、親のステートに合わせて実行する計算処理が実行されてしまっている。
//レンダリング時
親コンポーネントが呼ばれました
2乗する関数が実行されました
子コンポーネントが呼ばれました
//親ボタンclick時
親コンポーネントが呼ばれました
2乗する関数が実行されました
//子ボタンclick時
親コンポーネントが呼ばれました
2乗する関数が実行されました //再レンダリング不必要
子コンポーネントが呼ばれました
コード(最適化後)
import {useState, memo, useCallback, useMemo} = "react"
const Parent = () => {
const [count, setCount] = useState(0);
const [childCount, setChildCount] = useState(0);
consol.log("親コンポーネントが呼ばれました");
const onClick = () => {
setCount(count + 1);
}
const squar = (count) => {
console.log("2乗する関数が実行されました")
return count * count
}
const squarCount = useMemo(sqrar(count),[count]);
const onClildClick = useCallback(() => {
setChildCount(childCount + 1);
},[childCount])
return(
<div>
<div>{count}</div>
<div>{squarCount}</div>
<button onClick={onClick}>Click</button>
<ChildComponent onClick={onClildClick}/>
<div>{childCount}</div>
</div>
)
}
const ChildComponent = memo((props) => {
console.log("子コンポーネントが呼ばれました")
return(
<div>
<div>This is ChildComponent</div>
<button onClick={props.onChildClick}>Click!<button>
</div>
)
})
出力結果
//レンダリング時
親コンポーネントが呼ばれました
2乗する関数が実行されました
子コンポーネントが呼ばれました
//親ボタンclick時
親コンポーネントが呼ばれました
2乗する関数が実行されました
//子ボタンclick時
親コンポーネントが呼ばれました
子コンポーネントが呼ばれました
useMemoの使うタイミング
- 数値処理を代入する変数があるとき
Discussion