🐷

ReactのuseMemoについてのコンポーネント考察メモ

2022/08/22に公開

useMemoはどれぐらいコンポーネントのRe-evaluateを避けるかについての考察です

useMemoは、必要の時だけコンポーネントのRe-evaluateを行う、というhookです

まずはuseMemoなしのサンプルです

コンポーネント構成はこちらです

コードはこちらです(ButtonはUIなので省略します)

App.js
function App() {
	const [showParagraph, setShowParagraph] = useState(false);

	console.log('APP RUNNING');

	function toggleParagraphHandler() {
		setShowParagraph((prevShowParagraph) => !prevShowParagraph);
	}

	return (
		<div className="App">
			<h1>Hello CodeSandbox</h1>
			<Output show={showParagraph} />
			<Button onClick={toggleParagraphHandler}>Toggle Paragraph</Button>
		</div>
	);
}
Output.js
function Output(props) {
	console.log('OUTPUT RUNNING');
	return <Paragraph show={props.show} />
}

export default Output;
Paragraph.js
function Pharagraph(props) {
	console.log('PARAGRAPH RUNNING');
	return <p>{props.show && 'This is new'}</p>;
}

export default Paragraph;

Output.jsとParagraph.jsの間は、結構無駄ですが
今回の実験のために敢えてこのような構成を使います

そして、コードを起動してみると

ログをテキストしてみます

# 起動
APP RUNNING
DEMOOUTPUT RUNNING
PARAGRAPH RUNNING

# ボタンをクリック
APP RUNNING
DEMOOUTPUT RUNNING
PARAGRAPH RUNNING

# ボタンをクリック
APP RUNNING
DEMOOUTPUT RUNNING
PARAGRAPH RUNNING

ご覧の通り、毎回ボタンをクリックすると、関連あるコンポーネントは全部再起動したようです
(今回はButton.jsを省きますが、もしButton.jsにconsole.logを入れたら、Buttonのログも見れます)

ちなみに、props.showを削除して、Paragraph.jsに固定のテキストを入れても
同じ量なログが飛んできますので

それは大変災難ですね

ここで、useMemo()でどれぐらいログを軽減できるのかを検証してみます

そのままuseMemo使います

Output.jsのexportにReact.Memoを入れます
(Paragraph.jsに入れなくていいです、もしOutputにRe-evaluateが行わなかったら、Paragraphにも発生しません)

Output.js
function Output(props) {
	console.log('OUTPUT RUNNING');
	return <Paragraph show={props.show} />
}

-export default Output;
+export default React.memo(Output);

検証結果は同じく

# 起動
APP RUNNING
DEMOOUTPUT RUNNING
PARAGRAPH RUNNING

# ボタンをクリック
APP RUNNING
DEMOOUTPUT RUNNING
PARAGRAPH RUNNING

# ボタンをクリック
APP RUNNING
DEMOOUTPUT RUNNING
PARAGRAPH RUNNING

show={false}を使ってみる

つまり、ボタンを何回クリックしても
文字が表れないままになります

App.js
function App() {
	const [showParagraph, setShowParagraph] = useState(false);

	console.log('APP RUNNING');

	function toggleParagraphHandler() {
		setShowParagraph((prevShowParagraph) => !prevShowParagraph);
	}

	return (
		<div className="App">
			<h1>Hello CodeSandbox</h1>
-			<Output show={showParagraph} />
+			<Output show={false} />
			<Button onClick={toggleParagraphHandler}>Toggle Paragraph</Button>
		</div>
	);
}
Output.js
function Output(props) {
	console.log('OUTPUT RUNNING');
	return <Paragraph show={props.show} />
}

export default React.memo(Output);
Paragraph.js
function Pharagraph(props) {
	console.log('PARAGRAPH RUNNING');
	return <p>{props.show && 'This is new'}</p>;
}

export default React.memo(Paragraph);

検証結果は以下です

# 起動
APP RUNNING
DEMOOUTPUT RUNNING
PARAGRAPH RUNNING

# ボタンをクリック
APP RUNNING

# ボタンをクリック
APP RUNNING

すごく大量のログを減りましたが、画面が変わったからダメです

props.childrenを使ってみる

<Output show={false} /><Output show={showParagraph} />に戻り、続いて修正します

App.js
function App() {
	const [showParagraph, setShowParagraph] = useState(false);

	console.log('APP RUNNING');

	function toggleParagraphHandler() {
		setShowParagraph((prevShowParagraph) => !prevShowParagraph);
	}

	return (
		<div className="App">
			<h1>Hello CodeSandbox</h1>
			<Output show={showParagraph} />
			<Button onClick={toggleParagraphHandler}>Toggle Paragraph</Button>
		</div>
	);
}
Output.js
function Output(props) {
	console.log('OUTPUT RUNNING');
-	return <Paragraph show={props.show} />
+	return <Paragraph>{props.show && 'This is new'}</Paragraph>;
}

export default React.memo(Output);
Paragraph.js
function Pharagraph(props) {
	console.log('PARAGRAPH RUNNING');
-	return <p>{props.show && 'This is new'}</p>;
+	return <p>{props.children}</p>;
}

export default React.memo(Paragraph);

ログは以下です

# 起動
APP RUNNING
DEMOOUTPUT RUNNING
PARAGRAPH RUNNING

# ボタンをクリック
APP RUNNING
DEMOOUTPUT RUNNING
PARAGRAPH RUNNING

# ボタンをクリック
APP RUNNING
DEMOOUTPUT RUNNING
PARAGRAPH RUNNING

Discussion