Reactの木構造とrender
- stateが変わると再レンダリングされる
- 親のstateが変わると全ての子が再レンダリングされる
React.memoの活用
- 親のstateが変わっても子に渡すpropsの値に変化がなければ再レンダリングしない
React.memoの基本構文
- コンポーネントごとに設定する
import React, {FC} from 'react';
interface Props {
trigger: number
}
// React.memo()でラップする
const MemoChild: FC<Props> = React.memo(({trigger}) => {
// なんらかの重い処理
return (
<div>Memo Component: {trigger}</div>
)
})
export default MemoChild;
サンプルコードで動作確認
import React, {FC, useState} from 'react';
import {SfcChild, MemoChild, DeepEqualMemoChild} from "./components";
export interface Obj {
deepTrigger: number
}
const App: FC = () => {
const [sfcTrigger, setSfcTrigger] = useState<number>(0)
const [memoTrigger, setMemoTrigger] = useState<number>(0)
const countUpSfc = () => {
setSfcTrigger(prevState => prevState + 1)
}
const countUpMemo = () => {
setMemoTrigger(prevState => prevState + 1)
}
return (
<div>
<h2>React Memo Test</h2>
<button onClick={countUpSfc}>SFC</button>
<button onClick={countUpMemo}>Memo</button>
<SfcChild trigger={sfcTrigger} />
<MemoChild trigger={memoTrigger} />
</div>
);
};
export default App;
import React, {FC} from 'react';
interface Props {
trigger: number
}
const StatelessFunctionalComponent: FC<Props> = ({trigger}) => {
const startTime = performance.now()
for (let i=0; i < 10000; i++) {
// 16桁の文字列を乱数生成
const S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
const N=16
Array.from(Array(N)).map(()=>S[Math.floor(Math.random()*S.length)]).join('')
}
const endTime = performance.now()
console.log(`SFC: ${endTime - startTime} milliseconds`)
return (
<div>Stateless Functional Component: {trigger}</div>
);
};
export default StatelessFunctionalComponent;
import React, {FC} from 'react';
interface Props {
trigger: number
}
const ReactMemoComponent: FC<Props> = React.memo(({trigger}) => {
const startTime = performance.now();
for (let i=0; i < 10000; i++) {
// 16桁の文字列を乱数生成
const S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
const N=16
Array.from(Array(N)).map(()=>S[Math.floor(Math.random()*S.length)]).join('')
}
const endTime = performance.now()
console.log(`Memo: ${endTime - startTime} milliseconds`)
return (
<div>React Memo Component: {trigger}</div>
)
})
export default ReactMemoComponent;
コンソールログを確認しながら、各ボタンを押して動作を確認する。
SFCボタンを押す
→StatelessFunctionalComponent
のログだけ流れてくる
→StatelessFunctionalComponent
だけ再描画されている(ReactMemoComponent
は再描画されていない)
Memoボタンを押す
→StatelessFunctionalComponent
とReactMemoComponent
のログが流れてくる
→StatelessFunctionalComponent
とReactMemoComponent
の両方が再描画されている
Deep Equalの活用
- 比較関数を書く
- trueを返すとレンダリングしない
- falseを返すとレンダリングする
サンプルコードで動作確認
import React, {FC, useState} from 'react';
import {SfcChild, MemoChild, DeepEqualMemoChild} from "./components";
export interface Obj {
deepTrigger: number
}
const App: FC = () => {
const [sfcTrigger, setSfcTrigger] = useState<number>(0)
const [memoTrigger, setMemoTrigger] = useState<number>(0)
const [obj, setObject] = useState<Obj>({deepTrigger: 0})
const countUpSfc = () => {
setSfcTrigger(prevState => prevState + 1)
}
const countUpMemo = () => {
setMemoTrigger(prevState => prevState + 1)
}
const countUpDeepTrigger = () => {
setObject(prevState => {
return {deepTrigger: prevState.deepTrigger + 1}
})
}
return (
<div>
<h2>React Memo Test</h2>
<button onClick={countUpSfc}>SFC</button>
<button onClick={countUpMemo}>Memo</button>
<button onClick={countUpDeepTrigger}>Deep Equal Memo</button>
<SfcChild trigger={sfcTrigger} />
<MemoChild trigger={memoTrigger} />
<DeepEqualMemoChild trigger={memoTrigger} obj={obj}/>
</div>
);
};
export default App;
import React, {FC} from 'react';
import {Obj} from "../App";
interface Props {
trigger: number
obj: Obj
}
const DeepEqualMemoComponent: FC<Props> = React.memo(({trigger, obj}) => {
const startTime = performance.now()
for (let i=0; i < 10000; i++) {
// 16桁の文字列を乱数生成
const S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
const N=16
Array.from(Array(N)).map(()=>S[Math.floor(Math.random()*S.length)]).join('')
}
const endTime = performance.now()
console.log(`Deep Equal Memo: ${endTime - startTime} milliseconds`)
return (
<div>Deep Equal Memo Component: {trigger}</div>
)
}, (prevProps: Props, nextProps: Props) => {
const prevDeepTrigger = prevProps.obj.deepTrigger
const nextDeepTrigger = nextProps.obj.deepTrigger
return (prevDeepTrigger === nextDeepTrigger)
})
export default DeepEqualMemoComponent;
SFCボタンを押す
→StatelessFunctionalComponent
のログだけ流れてくる
→StatelessFunctionalComponent
だけ再描画されている
→ReactMemoComponent
、DeepEqualMemoComponent
は再描画されていない
Memoボタンを押す
→StatelessFunctionalComponent
とReactMemoComponent
のログが流れてくる
→StatelessFunctionalComponent
とReactMemoComponent
が再描画されている
→DeepEqualMemoComponent
は再描画されていない
→memoTrigger
も受け取っているが、obj
の変更を検知しない限り再レンダリングは行わない
Deep Equal Memoボタンを押す
→StatelessFunctionalComponent
とDeepEqualMemoComponent
のログが流れてくる
→StatelessFunctionalComponent
とDeepEqualMemoComponent
が再描画されている
→ReactMemoComponent
は再描画されていない