useMemo γ―γ€γƒ¬γƒ³γƒ€γƒΌι–“γ§θ¨ˆοΏ½?οΏ½η΅ζžœγ‚’γ‚­γƒ£γƒƒγ‚·γƒ₯γ™γ‚‹γŸγ‚οΏ½? React フックです。

const cachedValue = useMemo(calculateValue, dependencies)

γƒͺフゑレンス

useMemo(calculateValue, dependencies)

γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?γƒˆγƒƒγƒ—γƒ¬γƒ™γƒ«γ§ useMemo γ‚’ε‘Όγ³ε‡Ίγ—γ¦γ€γƒ¬γƒ³γƒ€γƒΌι–“γ§θ¨ˆοΏ½?�をキャッシγƒ₯します。

import { useMemo } from 'react';

function TodoList({ todos, tab }) {
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
);
// ...
}

さらに例を見る

εΌ•ζ•°

  • calculateValue: キャッシγƒ₯γ—γŸγ„ε€€γ‚’θ¨ˆοΏ½?οΏ½γ™γ‚‹ι–’ζ•°γ€‚η΄”ι–’ζ•°γ§γ€εΌ•ζ•°γ‚’ε–γ‚‰γšγ€δ»»ζ„οΏ½?εž‹οΏ½?何らか�?ε€€γ‚’θΏ”γ™εΏ…θ¦γŒγ‚γ‚ŠγΎγ™γ€‚React γ―εˆε›žγƒ¬γƒ³γƒ€γƒΌδΈ­γ«γ“οΏ½?ι–’ζ•°γ‚’ε‘Όγ³ε‡Ίγ—γΎγ™γ€‚ζ¬‘ε›žδ»₯降�?レンダーでは、直前�?レンダーと dependencies γŒε€‰εŒ–γ—γ¦γ„γͺγ‘γ‚Œγ°γ€εŒγ˜ε€€γ‚’ε†εΊ¦θΏ”γ—γΎγ™γ€‚dependencies γŒε€‰εŒ–γ—γ¦γ„γ‚Œγ°γ€calculateValue を呼び出してそ�?η΅ζžœγ‚’θΏ”γ—γ€εŒζ™‚γ«γ€εΎŒγ‹γ‚‰ε†εˆ©η”¨γ™γ‚‹γŸγ‚γ«γοΏ½?η΅ζžœγ‚’δΏε­˜γ—γΎγ™γ€‚

  • dependencies: calculateValue οΏ½?γ‚³γƒΌγƒ‰ε†…γ§ε‚η…§γ•γ‚Œγ¦γ„γ‚‹γ™γΉγ¦οΏ½?γƒͺをクティブ倀�?ι…εˆ—γ€‚γƒͺをクティブ倀には、props、stateγ€γŠγ‚ˆγ³γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆζœ¬δ½“γ§η›΄ζŽ₯οΏ½?οΏ½θ¨€γ•γ‚Œγ¦γ„γ‚‹γ™γΉγ¦οΏ½?ε€‰ζ•°γ¨ι–’ζ•°γŒε«γΎγ‚ŒγΎγ™γ€‚γƒͺγƒ³γ‚ΏγŒ React 向けに設�?οΏ½γ•γ‚Œγ¦γ„γ‚‹ε ΄εˆγ―γ€γ™γΉγ¦οΏ½?γƒͺγ‚’γ‚―γƒ†γ‚£γƒ–ε€€γŒζ­£γ—γδΎε­˜ε€€γ¨γ—γ¦ζŒ‡οΏ½?οΏ½γ•γ‚Œγ¦γ„γ‚‹γ‹γ‚’η’Ίθͺγ—γΎγ™γ€‚δΎε­˜ι…εˆ—γ―、[dep1, dep2, dep3] οΏ½?γ‚ˆγ†γ«γ‚€γƒ³γƒ©γ‚€γƒ³γ§θ¨˜θΏ°γ•γ‚Œγ€ι…εˆ—οΏ½?長さは一�?οΏ½γ§γ‚γ‚‹εΏ…θ¦γŒγ‚γ‚ŠγΎγ™γ€‚ε„δΎε­˜ε€€γ―γ€Object.is γ‚’η”¨γ„γ¦γ€ε‰ε›žοΏ½?ε€€γ¨ζ―”θΌƒγ•γ‚ŒγΎγ™γ€‚

θΏ”γ‚Šε€€

εˆε›žοΏ½?レンダーでは、引数γͺしで calculateValue γ‚’ε‘Όγ³ε‡Ίγ—γŸη΅ζžœγŒγ€useMemo οΏ½?θΏ”γ‚Šε€€γ¨γͺγ‚ŠγΎγ™γ€‚

ζ¬‘ε›žδ»₯降�?γƒ¬γƒ³γƒ€γƒΌγ§γ―γ€δΎε­˜ι…εˆ—γŒε€‰εŒ–γ—γ¦γ„γͺγ„ε ΄εˆγ―γ€δ»₯前�?γƒ¬γƒ³γƒ€γƒΌγ§δΏε­˜γ•γ‚ŒγŸε€€γ‚’θΏ”γ—γΎγ™γ€‚ε€‰εŒ–γ—γ¦γ„γ‚‹ε ΄εˆγ―γ€calculateValue を再度呼び出し、そ�?η΅ζžœγ‚’γοΏ½?まま返します。

注意点

  • useMemo はフックγͺοΏ½?γ§γ€γ‚«γ‚Ήγ‚Ώγƒ γƒ•γƒƒγ‚―γ‹γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?γƒˆγƒƒγƒ—γƒ¬γƒ™γƒ«γ§γ—γ‹ε‘Όγ³ε‡Ίγ™γ“γ¨γŒγ§γγΎγ›γ‚“γ€‚γƒ«γƒΌγƒ—γ‚„ζ‘δ»Άεˆ†ε²οΏ½?δΈ­γ§ε‘Όγ³ε‡Ίγ™γ“γ¨γ―γ§γγΎγ›γ‚“γ€‚γ‚‚γ—γƒ«γƒΌγƒ—γ‚„ζ‘δ»Άεˆ†ε²οΏ½?δΈ­γ§ε‘Όγ³ε‡Ίγ—γŸγ„ε ΄εˆγ―γ€ζ–°γ—γ„γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«εˆ‡γ‚Šε‡Ίγ—γ¦γ€γοΏ½?中に state を移動させてください。
  • Strict Mode では、純粋でγͺγ„ι–’ζ•°γ‚’θ¦‹γ€γ‘γ‚„γ™γγ™γ‚‹γŸγ‚γ«γ€θ¨ˆοΏ½?οΏ½ι–’ζ•° (calculateValue) が 2 εΊ¦ε‘Όγ³ε‡Ίγ•γ‚ŒγΎγ™γ€‚γ“γ‚Œγ―γ€ι–‹η™Ίζ™‚οΏ½?み�?ζŒ™ε‹•γ§γ€ζœ¬η•ͺγ§γ―ε½±ιŸΏγ―δΈŽγˆγΎγ›γ‚“γ€‚γ‚‚γ—γ€θ¨ˆοΏ½?οΏ½ι–’ζ•°γŒη΄”η²‹γ§γ‚γ‚Œγ°οΌˆη΄”η²‹γ§γ‚γ‚‹γΉγγ§γ™οΌ‰γ€2 ε›žε‘Όγ³ε‡Ίγ•γ‚Œγ¦γ‚‚γ‚³γƒΌγƒ‰γ«ε½±ιŸΏγ―γ‚γ‚ŠγΎγ›γ‚“γ€‚2 ε›žοΏ½?呼び出し�?うけ、一方�?ε‘Όγ³ε‡Ίγ—η΅ζžœγ―η„‘θ¦–γ•γ‚ŒγΎγ™γ€‚
  • η‰Ήεˆ₯γͺη†η”±γŒγͺγ„ι™γ‚Šγ€γ‚­γƒ£γƒƒγ‚·γƒ₯γ•γ‚ŒγŸε€€γŒη ΄ζ£„γ•γ‚Œγ‚‹γ“γ¨γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚γ‚­γƒ£γƒƒγ‚·γƒ₯γŒη ΄ζ£„γ•γ‚Œγ‚‹γ‚±γƒΌγ‚ΉοΏ½?δΎ‹γ¨γ—γ¦γ―γ€ι–‹η™Ίζ™‚γ«γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?γƒ•γ‚‘γ‚€γƒ«γ‚’η·¨ι›†γ—γŸε ΄εˆγŒγ‚γ‚ŠγΎγ™γ€‚γΎγŸγ€ι–‹η™Ίζ™‚γŠγ‚ˆγ³ζœ¬η•ͺζ™‚γ«γ€εˆε›žγƒžγ‚¦γƒ³γƒˆδΈ­γ«γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒγ‚΅γ‚Ήγƒšγƒ³γƒ‰γ™γ‚‹γ¨γ€γ‚­γƒ£γƒƒγ‚·γƒ₯γ―η ΄ζ£„γ•γ‚ŒγΎγ™γ€‚ε°†ζ₯ηš„γ«γ―γ€γ‚­γƒ£γƒƒγ‚·γƒ₯γŒη ΄ζ£„γ•γ‚Œγ‚‹γ“γ¨γ‚’ε‰ζγ¨γ—γŸζ©Ÿθƒ½γŒ React γ«θΏ½εŠ γ•γ‚Œγ‚‹ε―θƒ½ζ€§γŒγ‚γ‚ŠγΎγ™γ€‚δΎ‹γˆγ°γ€ε°†ζ₯ηš„γ«οΏ½?想γƒͺγ‚ΉγƒˆγŒη΅„γΏθΎΌγΏγ§γ‚΅γƒγƒΌγƒˆγ•γ‚ŒγŸε ΄εˆγ€οΏ½?想テーブル�?ビγƒ₯γƒΌγƒγƒΌγƒˆγ‹γ‚‰γ‚Ήγ‚―γƒ­γƒΌγƒ«γ‚’γ‚¦γƒˆγ—γŸι …οΏ½?は、キャッシγƒ₯γ‚’η ΄ζ£„γ™γ‚‹γ‚ˆγ†γ«γͺγ‚‹γ‹γ‚‚γ—γ‚ŒγΎγ›γ‚“γ€‚γ“οΏ½?γ‚ˆγ†γͺζŒ™ε‹•γ―γ€γƒ‘γƒ•γ‚©γƒΌγƒžγƒ³γ‚Ήζœ€ι©εŒ–οΏ½?みを�?ηš„γ¨γ—γ¦ useMemo γ‚’δ½Ώγ£γ¦γ„γ‚‹ε ΄εˆγ«γ―ε•ι‘Œγ‚γ‚ŠγΎγ›γ‚“γ€‚γ—γ‹γ—γ€δ»–οΏ½?οΏ½?ηš„γ§εˆ©η”¨γ—γ¦γ„γ‚‹ε ΄εˆγ―γ€state 倉数 γ‚„ ref γ‚’εˆ©η”¨γ—γŸζ–ΉγŒθ‰―γ„γ‹γ‚‚γ—γ‚ŒγΎγ›γ‚“γ€‚

補袳

こ�?γ‚ˆγ†γͺθΏ”γ‚Šε€€οΏ½?キャッシγƒ₯γ―γ€γƒ‘γƒ’εŒ– (memoization) としてηŸ₯γ‚‰γ‚Œγ¦γŠγ‚Šγ€γγ‚ŒγŒγ“οΏ½?γƒ•γƒƒγ‚―γŒ useMemo という名前である理由です。


使用法

ι«˜γ‚³γ‚Ήγƒˆγͺε†θ¨ˆοΏ½?�を避ける

θ€‡ζ•°γƒ¬γƒ³γƒ€γƒΌγ‚’θ·¨γ„γ§θ¨ˆοΏ½?�をキャッシγƒ₯γ™γ‚‹γ«γ―γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?γƒˆγƒƒγƒ—γƒ¬γƒ™γƒ«γ§ useMemo γ‚’ε‘Όγ³ε‡Ίγ—γ€θ¨ˆοΏ½?�をラップします。

import { useMemo } from 'react';

function TodoList({ todos, tab, theme }) {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
// ...
}

useMemo には、2 ぀�?εΌ•ζ•°γ‚’ζΈ‘γ™εΏ…θ¦γŒγ‚γ‚ŠγΎγ™γ€‚

  1. () => οΏ½?γ‚ˆγ†γ«γ€εΌ•ζ•°γ‚’ε–γ‚‰γšγ€ζ±‚γ‚γŸγ„θ¨ˆοΏ½?οΏ½η΅ζžœγ‚’θΏ”γ™θ¨ˆοΏ½?�閒数。
  2. γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆε†…γ«γ‚γ‚‹ε€€οΏ½?γ†γ‘γ€θ¨ˆοΏ½?οΏ½ι–’ζ•°ε†…γ§δ½Ώη”¨γ•γ‚Œγ¦γ„γ‚‹γ™γΉγ¦οΏ½?ε€€γ‚’ε«γ‚€γ€δΎε­˜ι…εˆ—γ€‚

εˆε›žγƒ¬γƒ³γƒ€γƒΌγ§γ―γ€useMemo γ‹γ‚‰θΏ”γ•γ‚Œγ‚‹ε€€γ―γ€θ¨ˆοΏ½?οΏ½ι–’ζ•°γ‚’ε‘Όγ³ε‡Ίγ—γŸη΅ζžœγ«γͺγ‚ŠγΎγ™γ€‚

ζ¬‘ε›žδ»₯降�?γƒ¬γƒ³γƒ€γƒΌγ§γ―γ€δ»Šε›žοΏ½?γƒ¬γƒ³γƒ€γƒΌζ™‚γ«ζΈ‘γ—γŸδΎε­˜ι…εˆ—γ¨γ€ε‰ε›žοΏ½?γƒ¬γƒ³γƒ€γƒΌζ™‚γ«ζΈ‘γ—γŸδΎε­˜ι…εˆ—γŒζ―”θΌƒγ•γ‚ŒγΎγ™γ€‚οΌˆObject.is γ§ζ―”θΌƒγ—γΎγ™γ€‚οΌ‰δΎε­˜ε€€οΏ½?γ„γšγ‚Œγ‚‚ε€‰εŒ–γ—γ¦γ„γͺγ„ε ΄εˆγ€useMemo はδ»₯ε‰γ«θ¨ˆοΏ½?οΏ½γ—γŸε€€γ‚’θΏ”γ—γΎγ™γ€‚ε€‰εŒ–γ—γ¦γ„γ‚‹ε ΄εˆγ―γ€ε†εΊ¦θ¨ˆοΏ½?�が�?οΏ½θ‘Œγ•γ‚Œγ€ζ–°γ—γ„ε€€γŒθΏ”γ•γ‚ŒγΎγ™γ€‚

γ€γΎγ‚Š useMemo γ―γ€δΎε­˜ι…εˆ—γŒε€‰εŒ–γ—γͺγ„ι™γ‚Šγ€θ€‡ζ•°οΏ½?γƒ¬γƒ³γƒ€γƒΌγ‚’θ·¨γ„γ§θ¨ˆοΏ½?οΏ½η΅ζžœγ‚’γ‚­γƒ£γƒƒγ‚·γƒ₯します。

γ“γ‚ŒγŒε½Ήγ«η«‹γ€ε ΄ι’γ‚’θ¦‹γ¦γΏγΎγ—γ‚‡γ†γ€‚

React γ§γ―ι€šεΈΈγ€ε†γƒ¬γƒ³γƒ€γƒΌγŒη™Ίη”Ÿγ™γ‚‹γŸγ³γ«γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆι–’ζ•°ε…¨δ½“γŒε†οΏ½?οΏ½θ‘Œγ•γ‚ŒγΎγ™γ€‚δΎ‹γˆγ°γ€δ»₯δΈ‹οΏ½? TodoList で、state γŒζ›΄ζ–°γ•γ‚ŒγŸγ‚Šγ€θ¦ͺから新しい props γ‚’ε—γ‘ε–γ£γŸγ‚Šγ—γŸε ΄εˆγ€filterTodos ι–’ζ•°γŒε†οΏ½?οΏ½θ‘Œγ•γ‚ŒγΎγ™γ€‚

function TodoList({ todos, tab, theme }) {
const visibleTodos = filterTodos(todos, tab);
// ...
}

ほとんど�?計�?οΏ½γ―ιžεΈΈγ«ι«˜ι€Ÿγ«ε‡¦η†γ•γ‚Œγ‚‹γŸγ‚γ€δ½•γ‹ε•ι‘Œγ«γͺγ‚‹γ“γ¨γ―ι€šεΈΈγ‚γ‚ŠγΎγ›γ‚“γ€‚γ—γ‹γ—γ€ε·¨ε€§γͺι…εˆ—γ‚’γƒ•γ‚£γƒ«γ‚Ώγƒͺγƒ³γ‚°γƒ»ε€‰ζ›γ—γ¦γ„γ‚‹ε ΄εˆγ‚„γ€ι«˜γ‚³γ‚Ήγƒˆγͺ計�?οΏ½γ‚’θ‘Œγ£γ¦γ„γ‚‹ε ΄εˆγ«γ―γ€γƒ‡γƒΌγ‚ΏγŒε€‰γ‚γ£γ¦γ„γͺγ‘γ‚Œγ°γ“γ‚Œγ‚‰οΏ½?計�?οΏ½γ‚’γ‚Ήγ‚­γƒƒγƒ—γ—γŸγγͺるでしょう。todos と tab οΏ½?ε€€γŒε‰ε›žοΏ½?γƒ¬γƒ³γƒ€γƒΌζ™‚γ¨εŒγ˜ε ΄εˆγ€ε…ˆγ»γ©οΏ½?γ‚ˆγ†γ«θ¨ˆοΏ½?οΏ½γ‚’ useMemo でラップすることで、δ»₯ε‰γ«θ¨ˆοΏ½?οΏ½γ—γŸ visibleTodos γ‚’ε†εˆ©η”¨γ™γ‚‹γ“γ¨γŒγ§γγ‚‹οΏ½?です。

こ�?γ‚ˆγ†γͺキャッシγƒ₯οΏ½?γ“γ¨γ‚’γ€γƒ‘γƒ’εŒ–γ¨ε‘Όγ³γΎγ™γ€‚

補袳

useMemo γ―γƒ‘γƒ•γ‚©γƒΌγƒžγƒ³γ‚Ήζœ€ι©εŒ–οΏ½?γŸγ‚γ«οΏ½?γΏεˆ©η”¨γ™γ‚‹γΉγγ§γ™γ€‚useMemo γ‚’ε€–γ™γ¨γ‚³γƒΌγƒ‰γŒε‹•δ½œγ—γͺγ„ε ΄εˆγ€γοΏ½?ζ Ήζœ¬ηš„γͺε•ι‘Œγ‚’θ¦‹γ€γ‘γ¦οΏ½?正してください。そ�?δΈŠγ§γ€γƒ‘γƒ•γ‚©γƒΌγƒžγƒ³γ‚Ήγ‚’ε‘δΈŠγ•γ›γ‚‹γŸγ‚γ« useMemo γ‚’θΏ½εŠ γ—γ¦γγ γ•γ„γ€‚

さらに深くηŸ₯γ‚‹

計�?οΏ½γ‚³γ‚ΉγƒˆγŒι«˜γ„γ‹γ©γ†γ‹γ‚’θ¦‹εˆ†γ‘γ‚‹ζ–Ήζ³•

δΈ€θˆ¬ηš„γ«γ€δ½•εƒγ‚‚οΏ½?γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’δ½œζˆγ—γŸγ‚Šγƒ«γƒΌγƒ—γ—γŸγ‚Šγ—γ¦γ„γͺγ„ι™γ‚Šγ€γŠγγ‚‰γι«˜δΎ‘γ§γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚γ‚ˆγ‚Šη’ΊδΏ‘γ‚’ζŒγ‘γŸγ„ε ΄εˆγ―γ€γ‚³γƒ³γ‚½γƒΌγƒ«γƒ­γ‚°γ‚’θΏ½εŠ γ—γ¦γ€γ‚³γƒΌγƒ‰οΏ½?οΏ½?οΏ½θ‘Œγ«γ‹γ‹γ£γŸζ™‚ι–“γ‚’θ¨ˆζΈ¬γ™γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚

console.time('filter array');
const visibleTodos = filterTodos(todos, tab);
console.timeEnd('filter array');

ζΈ¬οΏ½?οΏ½γ—γŸγ„γƒ¦γƒΌγ‚Άζ“δ½œοΌˆδΎ‹γˆγ°γ€ε…₯εŠ›γƒ•γ‚£γƒΌγƒ«γƒ‰γΈοΏ½?γ‚Ώγ‚€γƒ—οΌ‰γ‚’οΏ½?οΏ½θ‘Œγ—γΎγ™γ€‚γοΏ½?εΎŒγ€γ‚³γƒ³γ‚½γƒΌγƒ«γ« filter array: 0.15ms οΏ½?γ‚ˆγ†γͺγƒ­γ‚°γŒθ‘¨η€Ίγ•γ‚ŒγΎγ™γ€‚ε…¨δ½“οΏ½?γƒ­γ‚°ζ™‚ι–“γŒγ‹γͺγ‚ŠοΏ½?ι‡οΌˆδΎ‹γˆγ° 1ms δ»₯δΈŠοΌ‰γ«γͺγ‚‹ε ΄εˆγ€γοΏ½?計�?οΏ½γ‚’γƒ‘γƒ’εŒ–γ™γ‚‹ζ„ε‘³γŒγ‚γ‚‹γ‹γ‚‚γ—γ‚ŒγΎγ›γ‚“γ€‚οΏ½?�験として useMemo で計�?�をラップしてみて、そ�?ζ“δ½œγ«ε―Ύγ™γ‚‹εˆθ¨ˆζ™‚ι–“γŒζΈ›ε°‘γ—γŸγ‹γ©γ†γ‹γ‚’γƒ­γ‚°γ§η’Ίθͺγ§γγΎγ™γ€‚

console.time('filter array');
const visibleTodos = useMemo(() => {
return filterTodos(todos, tab); // Skipped if todos and tab haven't changed
}, [todos, tab]);
console.timeEnd('filter array');

useMemo γ―εˆε›žγƒ¬γƒ³γƒ€γƒΌγ‚’ι«˜ι€ŸεŒ–γ—γΎγ›γ‚“γ€‚ζ›΄ζ–°ζ™‚γ«δΈθ¦γͺ作ζ₯­γ‚’スキップするときに�?み役立けます。

γΎγŸγ€γ»γ¨γ‚“γ©οΏ½?ε ΄εˆγ«γ€γ‚γͺγŸγŒδ½Ώγ£γ¦γ„γ‚‹γƒžγ‚·γƒ³γ―γ€γƒ¦γƒΌγ‚ΆοΏ½?γƒžγ‚·γƒ³γ‚ˆγ‚Šι«˜ι€Ÿγ«ε‹•δ½œγ™γ‚‹γ§γ‚γ‚γ†γ“γ¨γ‚’εΏ˜γ‚Œγ¦γ―γ„γ‘γΎγ›γ‚“γ€‚γοΏ½?γŸγ‚γ€ζ„ε›³ηš„γ«ε‡¦η†ι€ŸεΊ¦γ‚’δ½ŽδΈ‹γ•γ›γ¦γƒ‘γƒ•γ‚©γƒΌγƒžγƒ³γ‚Ήγ‚’γƒ†γ‚Ήγƒˆγ™γ‚‹οΏ½?γŒθ‰―γ„γ§γ—γ‚‡γ†γ€‚δΎ‹γˆγ°γ€Chrome では CPU γ‚Ήγƒ­γƒƒγƒˆγƒͺングγ‚ͺγƒ—γ‚·γƒ§γƒ³γŒζδΎ›γ•γ‚Œγ¦γ„γΎγ™γ€‚

γΎγŸγ€ι–‹η™Ίη’°ε’ƒγ§οΏ½?γƒ‘γƒ•γ‚©γƒΌγƒžγƒ³γ‚ΉζΈ¬οΏ½?�では�?�全に正璺γͺη΅ζžœγ―εΎ—γ‚‰γ‚Œγͺγ„γ“γ¨γ«ζ³¨ζ„γ—γ¦γγ γ•γ„γ€‚οΌˆδΎ‹γˆγ°γ€Strict Mode がγ‚ͺン�?ε ΄εˆγ€ε„γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒ 1 度ではγͺく 2 εΊ¦γƒ¬γƒ³γƒ€γƒΌγ•γ‚Œγ‚‹γ“γ¨γŒγ‚γ‚ŠγΎγ™γ€‚οΌ‰ζœ€γ‚‚ζ­£η’Ίγ«γƒ‘γƒ•γ‚©γƒΌγƒžγƒ³γ‚Ήγ‚’θ¨ˆζΈ¬γ™γ‚‹γŸγ‚γ«γ―γ€γ‚’γƒ—γƒͺγ‚’ζœ¬η•ͺη’°ε’ƒη”¨γ«γƒ“γƒ«γƒ‰γ—γ€γƒ¦γƒΌγ‚ΆγŒζŒγ£γ¦γ„γ‚‹γ‚ˆγ†γͺγƒ‡γƒγ‚€γ‚Ήγ§γƒ†γ‚Ήγƒˆγ—γ¦γγ γ•γ„γ€‚

さらに深くηŸ₯γ‚‹

あらゆる場所に useMemo γ‚’θΏ½εŠ γ™γΉγγ‹οΌŸ

あγͺた�?γ‚’γƒ—γƒͺγŒγ“οΏ½?γ‚΅γ‚€γƒˆοΏ½?γ‚ˆγ†γ«γ€γ»γ¨γ‚“γ©οΏ½?γ‚€γƒ³γ‚Ώγƒ©γ‚―γ‚·γƒ§γƒ³γŒε€§γΎγ‹γͺγ‚‚οΏ½?οΌˆγƒšγƒΌγ‚Έε…¨δ½“γ‚„γ‚»γ‚―γ‚·γƒ§γƒ³ε…¨δ½“οΏ½?οΏ½?γζ›γˆγͺγ©οΌ‰γ§γ‚γ‚‹ε ΄εˆγ€γƒ‘γƒ’εŒ–γ―ι€šεΈΈδΈθ¦γ§γ™γ€‚δΈ€ζ–Ήγ€γ‚γͺた�?γ‚’γƒ—γƒͺγŒζη”»γ‚¨γƒ‡γ‚£γ‚ΏοΏ½?γ‚ˆγ†γͺγ‚‚οΏ½?で、ほとんど�?γ‚€γƒ³γ‚Ώγƒ©γ‚―γ‚·γƒ§γƒ³γŒη΄°γ‹γͺγ‚‚οΏ½?οΌˆε›³ε½’γ‚’η§»ε‹•γ•γ›γ‚‹γͺγ©οΌ‰γ§γ‚γ‚‹ε ΄εˆγ€γƒ‘γƒ’εŒ–γ―ιžεΈΈγ«ε½Ήγ«η«‹γ€γ§γ—γ‚‡γ†γ€‚

useMemo γ‚’εˆ©η”¨γ—γŸζœ€ι©εŒ–γŒεŠ›γ‚’η™ΊοΏ½?する�?は、δ»₯δΈ‹οΏ½?γ‚ˆγ†γͺ、ほん�?一部�?γ‚±γƒΌγ‚Ήγ«ι™γ‚‰γ‚ŒγΎγ™γ€‚

  • useMemo γ§θ‘Œγ†θ¨ˆοΏ½?οΏ½γŒθ‘—γ—γι…γγ€γ‹γ€γ€γοΏ½?δΎε­˜ε€€γŒγ»γ¨γ‚“γ©ε€‰εŒ–γ—γͺγ„ε ΄εˆγ€‚
  • 計�?οΏ½γ—γŸε€€γ‚’γ€memo γ§γƒ©γƒƒγƒ—γ•γ‚ŒγŸγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½? props γ«ζΈ‘γ™ε ΄εˆγ€‚γ“οΏ½?ε ΄εˆγ―γ€ε€€γŒε€‰εŒ–γ—γ¦γ„γͺγ„ε ΄εˆγ«γ―ε†γƒ¬γƒ³γƒ€γƒΌγ‚’γ‚Ήγ‚­γƒƒγƒ—γ—γŸγ„γ§γ—γ‚‡γ†γ€‚γƒ‘γƒ’εŒ–γ™γ‚‹γ“γ¨γ§γ€δΎε­˜ε€€γŒη•°γͺγ‚‹ε ΄εˆγ«οΏ½?γΏγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’ε†γƒ¬γƒ³γƒ€γƒΌγ•γ›γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚
  • そ�?ε€€γŒγ€εΎŒγ§δ½•γ‚‰γ‹οΏ½?フック�?δΎε­˜ε€€γ¨γ—γ¦δ½Ώη”¨γ•γ‚Œγ‚‹γ‚±γƒΌγ‚Ήγ€‚δΎ‹γˆγ°γ€εˆ₯οΏ½? useMemo οΏ½?計�?�硐果がそ�?ε€€γ«δΎε­˜γ—γ¦γ„γ‚‹ε ΄εˆγ‚„γ€useEffect がそ�?ε€€γ«δΎε­˜γ—γ¦γ„γ‚‹ε ΄εˆγͺどです。

γ“γ‚Œγ‚‰οΏ½?γ‚±γƒΌγ‚Ήδ»₯ε€–γ§γ―γ€θ¨ˆοΏ½?οΏ½γ‚’ useMemo でラップすることにパγƒͺγƒƒγƒˆγ―γ‚γ‚ŠγΎγ›γ‚“γ€‚γγ‚Œγ‚’θ‘Œγ£γ¦γ‚‚ι‡ε€§γͺοΏ½?�はγͺγ„γŸγ‚γ€ε€‹εˆ₯οΏ½?γ‚±γƒΌγ‚Ήγ‚’θ€ƒγˆγšγ«γ€ε―θƒ½γͺι™γ‚Šγ™γΉγ¦γ‚’γƒ‘γƒ’εŒ–γ™γ‚‹γ‚ˆγ†γ«γ—γ¦γ„γ‚‹γƒγƒΌγƒ γ‚‚γ‚γ‚ŠγΎγ™γ€‚γ“οΏ½?をプローチ�?デパγƒͺγƒƒγƒˆγ―γ€γ‚³γƒΌγƒ‰γŒθͺ­γΏγ«γγγͺγ‚‹γ¨γ„γ†η‚Ήγ§γ™γ€‚γΎγŸγ€γ™γΉγ¦οΏ½?γƒ‘γƒ’εŒ–γŒζœ‰εŠΉγ§γ‚γ‚‹γ‚γ‘γ§γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚δΎ‹γˆγ°γ€ζ―Žε›žε€‰εŒ–γ™γ‚‹ε€€γŒ 1 γ€ε­˜εœ¨γ™γ‚‹γ γ‘γ§γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆε…¨δ½“οΏ½?γƒ‘γƒ’εŒ–γŒη„‘ζ„ε‘³γ«γͺγ£γ¦γ—γΎγ†γ“γ¨γ‚‚γ‚γ‚ŠγΎγ™γ€‚

οΏ½?οΏ½ιš›γ«γ―γ€δ»₯δΈ‹οΏ½?いく぀か�?εŽŸε‰‡γ«εΎ“γ†γ“γ¨γ§γ€ε€šγοΏ½?γƒ‘γƒ’εŒ–γ‚’δΈθ¦γ«γ™γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚

  1. γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒδ»–οΏ½?γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’θ¦–θ¦šηš„γ«γƒ©γƒƒγƒ—γ™γ‚‹γ¨γγ―γ€γγ‚ŒγŒε­γ¨γ—γ¦ JSX を受けε…₯γ‚Œγ‚‹γ‚ˆγ†γ«γ—γΎγ™γ€‚γ“γ‚Œγ«γ‚ˆγ‚Šγ€γƒ©γƒƒγƒ‘γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒθ‡ͺθΊ«οΏ½? state を更新しても、React はそ�?ε­γ‚’ε†γƒ¬γƒ³γƒ€γƒΌγ™γ‚‹εΏ…θ¦γŒγͺいことをθͺθ­˜γ—ます。
  2. ローカル state γ‚’ε„ͺε…ˆγ—γ€εΏ…θ¦δ»₯上に state οΏ½?γƒͺγƒ•γƒˆγ‚’γƒƒγƒ—γ‚’θ‘Œγ‚γͺγ„γ‚ˆγ†γ«γ—γΎγ™γ€‚γƒ•γ‚©γƒΌγƒ γ‚„γ€γ‚’γ‚€γƒ†γƒ γŒγƒ›γƒγƒΌγ•γ‚Œγ¦γ„γ‚‹γ‹γ©γ†γ‹γ€γ¨γ„γ£γŸι »ηΉγ«ε€‰εŒ–γ™γ‚‹ state は、ツγƒͺγƒΌοΏ½?γƒˆγƒƒγƒ—γ‚„γ‚°γƒ­γƒΌγƒγƒ«οΏ½?ηŠΆζ…‹γƒ©γ‚€γƒ–γƒ©γƒͺγ«δΏζŒγ—γͺいでください。
  3. γƒ¬γƒ³γƒ€γƒΌγƒ­γ‚Έγƒƒγ‚―γ‚’η΄”η²‹γ«δΏγ‘γΎγ™γ€‚γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγŒε•ι‘Œγ‚’εΌ•γθ΅·γ“γ—γŸγ‚Šγ€δ½•γ‚‰γ‹οΏ½?οΏ½?γ«θ¦‹γˆγ‚‹θ¦–θ¦šηš„γͺη΅ζžœγ‚’η”Ÿγ˜γŸγ‚Šγ™γ‚‹ε ΄εˆγ€γγ‚Œγ―γ‚γͺた�?γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?バグです! γƒ‘γƒ’εŒ–γ‚’θΏ½εŠ γ™γ‚‹οΏ½?ではγͺく、バグを�?正します。
  4. state を更新する不要γͺγ‚¨γƒ•γ‚§γ‚―γƒˆγ‚’ιΏγ‘γ¦γγ γ•γ„γ€‚React γ‚’γƒ—γƒͺケーション�?γƒ‘γƒ•γ‚©γƒΌγƒžγƒ³γ‚Ήε•ι‘ŒοΏ½?ε€§ιƒ¨εˆ†γ―γ€γ‚¨γƒ•γ‚§γ‚―γƒˆε†…γ§οΏ½?ι€£ιŽ–ηš„γͺ state ζ›΄ζ–°γ«γ‚ˆγ£γ¦γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?γƒ¬γƒ³γƒ€γƒΌγŒδ½•εΊ¦γ‚‚εΌ•γθ΅·γ“γ•γ‚Œγ‚‹γŸγ‚γ«η”Ÿγ˜γΎγ™γ€‚
  5. γ‚¨γƒ•γ‚§γ‚―γƒˆγ‹γ‚‰δΈθ¦γͺδΎε­˜ε€€γ‚’γ§γγ‚‹γ γ‘ε‰Šι™€γ—γΎγ™γ€‚δΎ‹γˆγ°γ€γƒ‘γƒ’εŒ–γ™γ‚‹δ»£γ‚γ‚Šγ«γ€γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚„ι–’ζ•°γ‚’γ‚¨γƒ•γ‚§γ‚―γƒˆοΏ½?δΈ­γ‚„ε€–γ«η§»ε‹•γ•γ›γ‚‹γ γ‘γ§γ€η°‘ε˜γ«θ§£ζ±Ίγ§γγ‚‹ε ΄εˆγŒγ‚γ‚ŠγΎγ™γ€‚

γγ‚Œγ§γ‚‚η‰ΉοΏ½?οΏ½οΏ½?γ‚€γƒ³γ‚Ώγƒ©γ‚―γ‚·γƒ§γƒ³γŒι…γ„γ¨ζ„Ÿγ˜γ‚‹ε ΄εˆγ―γ€React Developer Tools οΏ½?プロフゑむラを使用して、ど�?γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ§οΏ½?γƒ‘γƒ’εŒ–γŒζœ€γ‚‚ζœ‰εŠΉγ‹γ‚’η’Ίθͺγ—γ€γγ“γ§γƒ‘γƒ’εŒ–γ‚’θ‘Œγ„γΎγ—γ‚‡γ†γ€‚γ“γ‚Œγ‚‰οΏ½?εŽŸε‰‡γ‚’οΏ½?οΏ½γ‚‹γ“γ¨γ§γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?γƒ‡γƒγƒƒγ‚°γ‚„η†θ§£γŒοΏ½?οΏ½ζ˜“γ«γͺγ‚‹γŸγ‚γ€εΈΈγ«εŽŸε‰‡γ«εΎ“γ†γ“γ¨γ‚’γŠγ™γ™γ‚γ—γΎγ™γ€‚ι•·ζœŸηš„γ«γ―γ€γ“οΏ½?ε•ι‘Œγ‚’δΈ€ζŒ™γ«θ§£ζ±Ίγ§γγ‚‹θ‡ͺε‹•ηš„γͺγƒ‘γƒ’εŒ–γ«γ€γ„γ¦η ”η©Άγ‚’θ‘Œγ£γ¦γ„γΎγ™γ€‚

useMemo と倀を直ζŽ₯計�?�すること�?違い

δΎ‹ 1/2:
useMemo γ‚’εˆ©η”¨γ—γ¦ε†θ¨ˆοΏ½?�をスキップする

こ�?例では filterTodos οΏ½?οΏ½?οΏ½θ£…γ«γ―δΊΊη‚Ίηš„γͺι…ε»ΆγŒε…₯っています。そ�?γŸγ‚γ€γƒ¬γƒ³γƒ€γƒΌδΈ­γ«ε‘Όγ³ε‡Ίγ™ JavaScript οΏ½?ι–’ζ•°οΏ½?ε‡¦η†γŒθ‘—γ—γι…γ„ε ΄εˆγ«γ€γ©γ†γͺるかを璺θͺγ§γγΎγ™γ€‚γ‚Ώγƒ–γ‚’εˆ‡γ‚Šζ›ΏγˆγŸγ‚Šγ€γƒ†γƒΌγƒžγ‚’εˆ‡γ‚Šζ›Ώγˆγ¦γΏγ¦γγ γ•γ„γ€‚

γ‚Ώγƒ–οΏ½?εˆ‡γ‚Šζ›ΏγˆγŒι…γζ„Ÿγ˜γ‚‰γ‚Œγ‚‹οΏ½?γ―γ€εˆ‡γ‚Šζ›Ώγˆγ«γ‚ˆγ£γ¦γ€ι…ε»ΆγŒε…₯っている filterTodos 閒数を再�?οΏ½θ‘Œγ•γ›γ¦γ—γΎγ£γ¦γ„γ‚‹γ‹γ‚‰γ§γ™γ€‚γ“οΏ½?ζŒ™ε‹•γ―θ€ƒγˆγ¦γΏγ‚Œγ°ε½“γŸγ‚Šε‰γ§γ€tab γŒε€‰εŒ–γ—γŸοΏ½?γͺγ‚‰γ€θ¨ˆοΏ½?�全体を再�?οΏ½θ‘Œγ™γ‚‹εΏ…θ¦γŒγ‚γ‚‹γ―γšγ§γ™γ€‚οΌˆγͺぜ 2 ε›žοΏ½?οΏ½θ‘Œγ•γ‚Œγ‚‹οΏ½?か気にγͺγ‚‹ε ΄εˆγ―γ€γ“γ‘γ‚‰γ‚’ε‚η…§γ—γ¦γγ γ•γ„οΌ‰

ζ¬‘γ«γ€γƒ†γƒΌγƒžγ‚’εˆ‡γ‚Šζ›Ώγˆγ¦γΏγΎγ—γ‚‡γ†γ€‚useMemo γŒγ‚γ‚‹γŠγ‹γ’γ§γ€δΊΊη‚Ίηš„γͺι…ε»ΆγŒε…₯γ£γ¦γ„γ‚‹γ«γ‚‚ι–’γ‚γ‚‰γšγ€ι«˜ι€Ÿγ«ε‹•δ½œγ—γ¦γ„γΎγ™οΌ todos と tab(useMemo οΏ½?δΎε­˜ι…εˆ—γ¨γ—γ¦ζΈ‘γ—γ¦γ„γ‚‹οΌ‰γŒγ€ε‰ε›žοΏ½?γƒ¬γƒ³γƒ€γƒΌζ™‚γ‹γ‚‰ε€‰εŒ–γ—γ¦γ„γͺγ„γŸγ‚γ€ι…ε»ΆγŒε…₯っている filterTodos οΏ½?ε‘Όγ³ε‡Ίγ—γŒγ‚Ήγ‚­γƒƒγƒ—γ•γ‚Œγ¦γ„γΎγ™γ€‚

import { useMemo } from 'react';
import { filterTodos } from './utils.js'

export default function TodoList({ todos, theme, tab }) {
  const visibleTodos = useMemo(
    () => filterTodos(todos, tab),
    [todos, tab]
  );
  return (
    <div className={theme}>
      <p><b>Note: <code>filterTodos</code> is artificially slowed down!</b></p>
      <ul>
        {visibleTodos.map(todo => (
          <li key={todo.id}>
            {todo.completed ?
              <s>{todo.text}</s> :
              todo.text
            }
          </li>
        ))}
      </ul>
    </div>
  );
}


γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?再レンダーをスキップする

useMemo γ―γ€ε­γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?再レンダー�?γƒ‘γƒ•γ‚©γƒΌγƒžγƒ³γ‚Ήγ‚’ζœ€ι©εŒ–γ™γ‚‹ιš›γ«γ‚‚ε½Ήγ«η«‹γ€γ“γ¨γŒγ‚γ‚ŠγΎγ™γ€‚γ“γ‚Œγ‚’θͺ¬ζ˜Žγ™γ‚‹γŸγ‚γ«γ€TodoList γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒγ€ε­γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½? List οΏ½? props として、visibleTodos γ‚’ζΈ‘γ™γ“γ¨γ‚’θ€ƒγˆγΎγ™γ€‚

export default function TodoList({ todos, tab, theme }) {
// ...
return (
<div className={theme}>
<List items={visibleTodos} />
</div>
);
}

props である theme γ‚’ε€‰εŒ–γ•γ›γ‚‹γ¨δΈ€ηž¬γ‚’γƒ—γƒͺγŒγƒ•γƒͺγƒΌγ‚Ίγ—γΎγ™γŒγ€<List /> γ‚’ JSX γ‹γ‚‰ε‰Šι™€γ™γ‚‹γ¨γ€ι«˜ι€Ÿγ«ε‹•δ½œγ™γ‚‹γ‚ˆγ†γ«γͺγ£γŸγ―γšγ§γ™γ€‚γ™γͺわけ、こ�? List γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«γ―ζœ€ι©εŒ–γ™γ‚‹δΎ‘ε€€γŒγ‚γ‚‹γ¨γ„γ†γ“γ¨γ§γ™γ€‚

ι€šεΈΈγ€γ‚γ‚‹γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒε†γƒ¬γƒ³γƒ€γƒΌγ•γ‚ŒγŸγ¨γγ―γ€γοΏ½?ε­γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚‚ε†εΈ°ηš„γ«γ™γΉγ¦ε†γƒ¬γƒ³γƒ€γƒΌγ•γ‚ŒγΎγ™γ€‚γ“γ‚ŒγŒγ€TodoList γŒη•°γͺγ‚‹ theme οΏ½?ε€€γ§ε†γƒ¬γƒ³γƒ€γƒΌγ•γ‚ŒγŸγ¨γγ€List γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚‚δΈ€η·’γ«ε†γƒ¬γƒ³γƒ€γƒΌγ•γ‚Œγ‚‹η†η”±γ§γ™γ€‚γ“οΏ½?ε‹•δ½œγ―γ€ε†γƒ¬γƒ³γƒ€γƒΌγ«γγ‚Œγ»γ©ε€šγοΏ½?計�?οΏ½γ‚³γ‚Ήγƒˆγ‚’εΏ…θ¦γ¨γ—γͺγ„γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«γ―ι©γ—γ¦γ„γΎγ™γ€‚γ—γ‹γ—γ€γ‚‚γ—ε†γƒ¬γƒ³γƒ€γƒΌγŒι…γ„γ¨εˆ†γ‹γ£γŸε ΄εˆγ―γ€List γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’ memo γ§ε›²γ†γ“γ¨γ§γ€δΈŽγˆγ‚‰γ‚ŒγŸ props γŒε‰ε›žοΏ½?γƒ¬γƒ³γƒ€γƒΌγ¨εŒγ˜γ§γ‚γ‚‹ε ΄εˆγ« List οΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ‚’γ‚Ήγ‚­γƒƒγƒ—γ™γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚

import { memo } from 'react';

const List = memo(function List({ items }) {
// ...
});

こ�?ε€‰ζ›΄γ«γ‚ˆγ£γ¦γ€props οΏ½?ε…¨ι …οΏ½?γŒε‰ε›žοΏ½?γƒ¬γƒ³γƒ€γƒΌγ¨η­‰γ—γ„ε ΄εˆγ«γ―γ€List οΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ―γ‚Ήγ‚­γƒƒγƒ—γ•γ‚Œγ‚‹γ‚ˆγ†γ«γͺγ‚ŠγΎγ™γ€‚γ“γ‚ŒγŒγ€θ¨ˆοΏ½?οΏ½οΏ½?キャッシγƒ₯γŒι‡θ¦γ«γͺる理由です! useMemo γ‚’δ½Ώγ‚γšγ« visibleTodos οΏ½?計�?οΏ½γ‚’θ‘Œγ†γ“γ¨γ‚’ζƒ³εƒγ—γ¦γΏγ¦γγ γ•γ„γ€‚

export default function TodoList({ todos, tab, theme }) {
// Every time the theme changes, this will be a different array...
const visibleTodos = filterTodos(todos, tab);
return (
<div className={theme}>
{/* ... so List's props will never be the same, and it will re-render every time */}
<List items={visibleTodos} />
</div>
);
}

上記�?例では、filterTodos ι–’ζ•°γŒζ―Žε›žη•°γͺγ‚‹ι…εˆ—γ‚’η”Ÿζˆγ—γΎγ™γ€‚οΌˆγ“γ‚Œγ―γ€{} というγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγƒͺγƒ†γƒ©γƒ«γŒγ€ζ―Žε›žζ–°γ—γ„γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’η”Ÿζˆγ™γ‚‹γ“γ¨γ¨δΌΌγ¦γ„γΎγ™γ€‚οΌ‰ι€šεΈΈγ“γ‚ŒγŒε•ι‘Œγ«γͺγ‚‹γ“γ¨γ―γ‚γ‚ŠγΎγ›γ‚“γŒγ€δ»Šε›žοΏ½?ε ΄εˆγ―γ€List οΏ½? props γŒζ―Žε›žεˆ₯οΏ½?倀にγͺってしまいます。そ�?γŸγ‚γ€memo γ«γ‚ˆγ‚‹ζœ€ι©εŒ–γŒζ„ε‘³γ‚’γͺさγͺくγͺってしまう�?です。ここで、useMemo γŒε½Ήγ«η«‹γ‘γΎγ™γ€‚

export default function TodoList({ todos, tab, theme }) {
// Tell React to cache your calculation between re-renders...
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab] // ...so as long as these dependencies don't change...
);
return (
<div className={theme}>
{/* ...List will receive the same props and can skip re-rendering */}
<List items={visibleTodos} />
</div>
);
}

visibleTodos οΏ½?計�?οΏ½γ‚’ useMemo でラップすることで、耇数�?再レンダー�?間でそ�?硐果が同じにγͺγ‚‹γ“γ¨γ‚’δΏθ¨Όγ§γγΎγ™οΌˆδΎε­˜ι…εˆ—γŒε€‰γ‚γ‚‰γͺγ„ι™γ‚ŠοΌ‰γ€‚ι€šεΈΈγ€η‰Ήεˆ₯γͺη†η”±γŒγͺγ‘γ‚Œγ°γ€θ¨ˆοΏ½?οΏ½γ‚’ useMemo γ§γƒ©γƒƒγƒ—γ™γ‚‹εΏ…θ¦γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚γ“οΏ½?例では、memo γ§ε›²γ‚γ‚ŒγŸγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«ε€€γ‚’ζΈ‘γ—γ¦γŠγ‚Šγƒ¬γƒ³γƒ€γƒΌοΏ½?γ‚Ήγ‚­γƒƒγƒ—γŒγ§γγ‚‹γ¨γ„γ†γ“γ¨γŒγ€γοΏ½?η‰Ήεˆ₯γͺη†η”±γ«γ‚γŸγ‚ŠγΎγ™γ€‚δ»–γ«γ‚‚ useMemo γ‚’θΏ½εŠ γ™γ‚‹ε‹•ζ©Ÿγ―γ„γγ€γ‹γ‚γ‚Šγ€γ“οΏ½?γƒšγƒΌγ‚Έγ§θ©³γ—γθ§£θͺ¬γ—ていきます。

さらに深くηŸ₯γ‚‹

個々�? JSX γƒŽγƒΌγƒ‰γ‚’γƒ‘γƒ’εŒ–γ™γ‚‹

List γ‚’ memo γ§γƒ©γƒƒγƒ—γ™γ‚‹δ»£γ‚γ‚Šγ«γ€<List /> JSX γƒŽγƒΌγƒ‰θ‡ͺ体を useMemo でラップしても構いません。

export default function TodoList({ todos, tab, theme }) {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
const children = useMemo(() => <List items={visibleTodos} />, [visibleTodos]);
return (
<div className={theme}>
{children}
</div>
);
}

ζŒ™ε‹•γ―εŒγ˜γ«γͺγ‚ŠγΎγ™γ€‚visibleTodos γŒε€‰εŒ–γ—γ¦γ„γͺγ„ε ΄εˆγ―γ€List γ―ε†γƒ¬γƒ³γƒ€γƒΌγ•γ‚ŒγΎγ›γ‚“γ€‚

<List items={visibleTodos} /> οΏ½?γ‚ˆγ†γͺ JSX γƒŽγƒΌγƒ‰γ―γ€{ type: List, props: { items: visibleTodos } } οΏ½?γ‚ˆγ†γͺγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ¨εŒγ˜γ§γ™γ€‚γ“οΏ½?γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’δ½œζˆγ™γ‚‹γ‚³γ‚Ήγƒˆγ―ιžεΈΈγ«ε°γ•γ„γ§γ™γŒγ€React はそ�?ε†…οΏ½?οΏ½γŒε‰ε›žοΏ½?ε†…οΏ½?οΏ½γ¨εŒγ˜γ‹γ©γ†γ‹γ―εˆ†γ‹γ‚ŠγΎγ›γ‚“γ€‚γοΏ½?γŸγ‚γ€React γ―γ€γƒ‡γƒ•γ‚©γƒ«γƒˆγ§ List γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’ε†γƒ¬γƒ³γƒ€γƒΌγ™γ‚‹οΏ½?です。

しかし、React γŒε‰ε›žοΏ½?γƒ¬γƒ³γƒ€γƒΌζ™‚γ¨ε…¨γεŒγ˜ JSX γ‚’θ¦‹γ€γ‘γŸε ΄εˆγ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ―θ‘Œγ„γΎγ›γ‚“γ€‚γ“γ‚Œγ―γ€JSX γƒŽγƒΌγƒ‰γŒγ‚€γƒŸγƒ₯ータブル (immutable) γ§γ‚γ‚‹γŸγ‚γ§γ™γ€‚JSX γƒŽγƒΌγƒ‰γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ―ζ™‚ι–“γŒη΅ŒιŽγ—γ¦γ‚‚ε€‰εŒ–γ™γ‚‹γ“γ¨γ―γͺγ„γŸγ‚γ€ε†γƒ¬γƒ³γƒ€γƒΌγ‚’γ‚Ήγ‚­γƒƒγƒ—γ—γ¦γ—γΎγ£γ¦ε•ι‘Œγ‚γ‚ŠγΎγ›γ‚“γ€‚γ—γ‹γ—γ€γ“γ‚ŒγŒζ©Ÿθƒ½γ™γ‚‹γ«γ―γ€γƒŽγƒΌγƒ‰γŒηœŸγ«ε…¨γεŒδΈ€οΏ½?γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ§γ‚γ‚‹εΏ…θ¦γŒγ‚γ‚Šγ€γ‚³γƒΌγƒ‰δΈŠγ§εŒγ˜γ‚ˆγ†γ«θ¦‹γˆγ‚‹γ γ‘γ§γ―δΈεεˆ†γ§γ™γ€‚γ“οΏ½?例では、useMemo οΏ½?γŠγ‹γ’γ§γ€γƒŽγƒΌγƒ‰γŒε…¨γεŒγ˜γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ¨γͺっている�?です。

useMemo を使って、JSX γƒŽγƒΌγƒ‰γ‚’ζ‰‹ε‹•γ§γƒ©γƒƒγƒ—γ™γ‚‹οΏ½?γ―δΈδΎΏγ§γ™γ€‚δΎ‹γˆγ°γ€ζ‘δ»Άδ»˜γγ§γƒ©γƒƒγƒ—γ™γ‚‹γ“γ¨γ―γ§γγΎγ›γ‚“γ€‚γοΏ½?γŸγ‚γ€ι€šεΈΈγ― useMemo で JSX γƒŽγƒΌγƒ‰γ‚’γƒ©γƒƒγƒ—γ™γ‚‹δ»£γ‚γ‚Šγ«γ€memo γ§γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’γ§γƒ©γƒƒγƒ—γ—γΎγ™γ€‚

ε†γƒ¬γƒ³γƒ€γƒΌγ‚’γ‚Ήγ‚­γƒƒγƒ—γ™γ‚‹ε ΄εˆγ¨ζ―Žε›žε†γƒ¬γƒ³γƒ€γƒΌγ‚’θ‘Œγ†ε ΄εˆοΏ½?違い

δΎ‹ 1/2:
useMemo と memo γ‚’εˆ©η”¨γ—γ¦ε†γƒ¬γƒ³γƒ€γƒΌγ‚’γ‚Ήγ‚­γƒƒγƒ—γ™γ‚‹

こ�?例では、List γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«γ―δΊΊη‚Ίηš„γͺι…ε»ΆγŒε…₯っています。そ�?γŸγ‚γ€γƒ¬γƒ³γƒ€γƒΌδΈ­γ«ε‘Όγ³ε‡Ίγ—γ¦γ„γ‚‹ React γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒθ‘—γ—γι…γ„ε ΄εˆοΏ½?ζŒ™ε‹•γ‚’η’Ίθͺγ§γγΎγ™γ€‚γ‚Ώγƒ–γ‚’ε€‰ζ›΄γ—γŸγ‚Šγ€γƒ†γƒΌγƒžγ‚’εˆ‡γ‚Šζ›ΏγˆγŸγ‚Šγ—γ¦γΏγ¦γγ γ•γ„γ€‚

γ‚Ώγƒ–οΏ½?εˆ‡γ‚Šζ›ΏγˆγŒι…γζ„Ÿγ˜γ‚‹οΏ½?γ―γ€ι…ε»ΆγŒε…₯っている List γ‚’ε†γƒ¬γƒ³γƒ€γƒΌγ•γ›γ¦γ—γΎγ£γ¦γ„γ‚‹γ‹γ‚‰γ§γ™γ€‚γ“γ‚Œγ―θ€ƒγˆγ¦γΏγ‚Œγ°ε½“η„Άγ§γ€tab γŒε€‰εŒ–γ—γŸοΏ½?で、ユーア�?ζ–°γ—γ„ιΈζŠžγ‚’η”»ι’γ«εζ˜ γ™γ‚‹εΏ…θ¦γŒγ‚γ‚ŠγΎγ™γ€‚

ζ¬‘γ«γ€γƒ†γƒΌγƒžγ‚’εˆ‡γ‚Šζ›Ώγˆγ¦γΏγΎγ—γ‚‡γ†γ€‚useMemo と memo γŒγ‚γ‚‹γŠγ‹γ’γ§γ€δΊΊη‚Ίηš„γͺι…ε»ΆγŒγ‚γ‚‹γ«γ‚‚ι–’γ‚γ‚‰γšγ€ι«˜ι€Ÿγ«ε‹•δ½œγ—γ¦γ„γΎγ™οΌ visibleItems ι…εˆ—γŒε‰ε›žοΏ½?γƒ¬γƒ³γƒ€γƒΌζ™‚γ‹γ‚‰ε€‰εŒ–γ—γ¦γ„γͺγ„γŸγ‚γ€List は再レンダーをスキップしています。visibleItems ι…εˆ—γŒε€‰εŒ–γ—γ¦γ„γͺい�?は、todos と tab(useMemo οΏ½?δΎε­˜ι…εˆ—γ¨γ—γ¦ζΈ‘γ—γ¦γ„γ‚‹οΌ‰γŒγ€ε‰ε›žοΏ½?γƒ¬γƒ³γƒ€γƒΌζ™‚γ‹γ‚‰ε€‰εŒ–γ—γ¦γ„γͺいからです。

import { useMemo } from 'react';
import List from './List.js';
import { filterTodos } from './utils.js'

export default function TodoList({ todos, theme, tab }) {
  const visibleTodos = useMemo(
    () => filterTodos(todos, tab),
    [todos, tab]
  );
  return (
    <div className={theme}>
      <p><b>Note: <code>List</code> is artificially slowed down!</b></p>
      <List items={visibleTodos} />
    </div>
  );
}


δ»–οΏ½?γƒ•γƒƒγ‚―γ«ζΈ‘γ™δΎε­˜ε€€γ‚’γƒ‘γƒ’εŒ–γ™γ‚‹

γ‚γ‚‹θ¨ˆοΏ½?οΏ½γŒγ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?ζœ¬δ½“γ§η›΄ζŽ₯δ½œζˆγ•γ‚ŒγŸγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ«δΎε­˜γ—γ¦γ„γ‚‹γ¨γ—γΎγ—γ‚‡γ†γ€‚

function Dropdown({ allItems, text }) {
const searchOptions = { matchMode: 'whole-word', text };

const visibleItems = useMemo(() => {
return searchItems(allItems, searchOptions);
}, [allItems, searchOptions]); // 🚩 Caution: Dependency on an object created in the component body
// ...

こ�?γ‚ˆγ†γͺγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’δΎε­˜ε€€γ¨γ—γ¦δ½Ώγ†γ¨γƒ‘γƒ’εŒ–οΏ½?ζ„ε‘³γŒγͺくγͺγ£γ¦γ—γΎγ„γΎγ™γ€‚γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒε†γƒ¬γƒ³γƒ€γƒΌγ•γ‚ŒγŸγ¨γγ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?ζœ¬δ½“γ«ε«γΎγ‚Œγ‚‹γ‚³γƒΌγƒ‰γ―γ™γΉγ¦ε†οΏ½?οΏ½θ‘Œγ•γ‚ŒγΎγ™γ€‚searchOptions γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’δ½œζˆγ™γ‚‹γ‚³γƒΌγƒ‰γ‚‚γ€ζ―Žε›žε†οΏ½?οΏ½θ‘Œγ•γ‚ŒγΎγ™γ€‚searchOptions は useMemo οΏ½?δΎε­˜ε€€γ§γ‚γ‚Šγ€ζ―Žε›žη•°γͺる倀とγͺγ‚‹γŸγ‚γ€δΎε­˜ε€€γŒε€‰εŒ–γ—γŸγ¨εˆ€ζ–­γ•γ‚Œγ€searchItems γŒζ―Žε›žε†θ¨ˆοΏ½?οΏ½γ•γ‚ŒγΎγ™γ€‚

γ“γ‚Œγ‚’οΏ½?正するには、searchOptions γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’δΎε­˜ι…εˆ—γ«ζΈ‘γ™ε‰γ«γ€searchOptions γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆθ‡ͺδ½“γ‚’γƒ‘γƒ’εŒ–γ—γΎγ—γ‚‡γ†γ€‚

function Dropdown({ allItems, text }) {
const searchOptions = useMemo(() => {
return { matchMode: 'whole-word', text };
}, [text]); // βœ… Only changes when text changes

const visibleItems = useMemo(() => {
return searchItems(allItems, searchOptions);
}, [allItems, searchOptions]); // βœ… Only changes when allItems or searchOptions changes
// ...

上記�?例では、text γŒε€‰εŒ–γ—γͺγ‘γ‚Œγ°γ€searchOptions γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚‚ε€‰εŒ–γ—γΎγ›γ‚“γ€‚γ—γ‹γ—γ€γ•γ‚‰γ«θ‰―γ„οΏ½?正方法は、searchOptions γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆοΏ½?οΏ½?�言を useMemo οΏ½?計�?οΏ½ι–’ζ•°οΏ½?中に移動することです。

function Dropdown({ allItems, text }) {
const visibleItems = useMemo(() => {
const searchOptions = { matchMode: 'whole-word', text };
return searchItems(allItems, searchOptions);
}, [allItems, text]); // βœ… Only changes when allItems or text changes
// ...

γ“γ‚Œγ§γ€θ¨ˆοΏ½?οΏ½γŒη›΄ζŽ₯ text γ«δΎε­˜γ™γ‚‹γ‚ˆγ†γ«γͺγ‚ŠγΎγ—γŸγ€‚οΌˆtext γ―ζ–‡ε­—εˆ—γͺοΏ½?γ§γ€Œζ„ε›³γ›γšγ€ε€‰εŒ–γ—γ¦γ—γΎγ†γ“γ¨γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚οΌ‰


ι–’ζ•°γ‚’γƒ‘γƒ’εŒ–γ™γ‚‹

Form γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒ memo γ§γƒ©γƒƒγƒ—γ•γ‚Œγ¦γ„γ‚‹γ¨γ—γΎγ™γ€‚ι–’ζ•°γ‚’ props として渑してみましょう。

export default function ProductPage({ productId, referrer }) {
function handleSubmit(orderDetails) {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
}

return <Form onSubmit={handleSubmit} />;
}

{} γŒη•°γͺγ‚‹γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’η”Ÿζˆγ™γ‚‹οΏ½?γ¨εŒζ§˜γ«γ€function() {} οΏ½?γ‚ˆγ†γͺι–’ζ•°οΏ½?�言や、() => {} οΏ½?γ‚ˆγ†γͺι–’ζ•°εΌγ‚‚γΎγŸγ€γƒ¬γƒ³γƒ€γƒΌγ”γ¨γ«η•°γͺγ‚‹ι–’ζ•°γ‚’η”Ÿζˆγ—γΎγ™γ€‚ζ–°γ—γ„ι–’ζ•°γŒη”Ÿζˆγ•γ‚Œγ‚‹γ“γ¨θ‡ͺδ½“γ―ε•ι‘Œγ§γ―γͺγγ€ιΏγ‘γ‚‹γΉγγ“γ¨γ§γ‚‚γ‚γ‚ŠγΎγ›γ‚“γ€‚γ—γ‹γ—γ€Form γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒγƒ‘γƒ’εŒ–γ•γ‚Œγ¦γ„γ‚‹ηŠΆζ³γ§γ―γ€Form οΏ½? props γ«ζΈ‘γ™ε€€γŒε€‰γ‚γ£γ¦γ„γͺγ„ε ΄εˆγ― Form οΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ‚’γ‚Ήγ‚­γƒƒγƒ—γ—γŸγ„γ¨θ€ƒγˆγ‚‹γ§γ—γ‚‡γ†γ€‚ζ―Žε›žη•°γͺγ‚‹ε€€γŒ props γ«γ‚γ‚‹γ¨γ€γƒ‘γƒ’εŒ–γ―η„‘ζ„ε‘³γ«γͺってしまいます。

useMemo γ§ι–’ζ•°γ‚’γƒ‘γƒ’εŒ–γ™γ‚‹ε ΄εˆγ―γ€θ¨ˆοΏ½?οΏ½ι–’ζ•°γŒγ•γ‚‰γ«εˆ₯οΏ½?ι–’ζ•°γ‚’θΏ”γ™εΏ…θ¦γŒγ‚γ‚ŠγΎγ™γ€‚

export default function Page({ productId, referrer }) {
const handleSubmit = useMemo(() => {
return (orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
};
}, [productId, referrer]);

return <Form onSubmit={handleSubmit} />;
}

γͺんだか不恰ε₯½γ§γ™γ­οΌ ι–’ζ•°οΏ½?γƒ‘γƒ’εŒ–γ―γ‚ˆγγ‚γ‚‹γ“γ¨γͺοΏ½?γ§γ€γγ‚Œε°‚η”¨οΏ½?η΅„γΏθΎΌγΏγƒ•γƒƒγ‚―γŒζδΎ›γ•γ‚Œγ¦γ„γΎγ™γ€‚δ½™θ¨ˆγͺι–’ζ•°οΏ½?ε…₯γ‚Œε­γ‚’ιΏγ‘γ‚‹γ«γ―γ€useMemo οΏ½?δ»£γ‚γ‚Šγ« useCallback で閒数をラップしましょう。

export default function Page({ productId, referrer }) {
const handleSubmit = useCallback((orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
}, [productId, referrer]);

return <Form onSubmit={handleSubmit} />;
}

上記�? 2 ぀�?例は�?�全に等侑です。useCallback οΏ½?パγƒͺγƒƒγƒˆγ―γ€δ½™θ¨ˆγͺι–’ζ•°οΏ½?ε…₯γ‚Œε­γŒδΈθ¦γ«γͺγ‚‹γ“γ¨γ γ‘γ§γ™γ€‚γγ‚Œδ»₯ε€–οΏ½?ι•γ„γ―δ½•γ‚‚γ‚γ‚ŠγΎγ›γ‚“γ€‚useCallback に぀いて�?詳細は、こけらを参照してください。


γƒˆγƒ©γƒ–γƒ«γ‚·γƒ₯ーティング

再レンダー�?たびに計�?�が 2 ε›žοΏ½?οΏ½θ‘Œγ•γ‚Œγ‚‹

Strict Mode γ§γ―γ€ζœ¬ζ₯ 1 ε›žγ γ‘ι–’ζ•°γŒε‘Όγ³ε‡Ίγ•γ‚Œγ‚‹γ¨γ“γ‚γ§γ€2 ε›žε‘Όγ³ε‡Ίγ•γ‚Œγ‚‹γ“γ¨γŒγ‚γ‚ŠγΎγ™γ€‚

function TodoList({ todos, tab }) {
// This component function will run twice for every render.

const visibleTodos = useMemo(() => {
// This calculation will run twice if any of the dependencies change.
return filterTodos(todos, tab);
}, [todos, tab]);

// ...

γ“γ‚Œγ―ζƒ³οΏ½?οΏ½ι€šγ‚ŠοΏ½?ζŒ™ε‹•γ§γ‚γ‚Šγ€γ“γ‚Œγ§γ‚³γƒΌγƒ‰γŒε£Šγ‚Œγ‚‹γ“γ¨γŒγ‚γ£γ¦γ―γ„γ‘γΎγ›γ‚“γ€‚

γ“γ‚Œγ―ι–‹η™Ίζ™‚οΏ½?み�?ζŒ™ε‹•γ§γ€ι–‹η™Ίθ€…γŒγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’η΄”η²‹γ«δΏγ€γŸγ‚γ«ε½Ήη«‹γ‘γΎγ™γ€‚ε‘Όγ³ε‡Ίγ—η΅ζžœοΏ½?うけ�? 1 γ€γŒζŽ‘η”¨γ•γ‚Œγ€γ‚‚γ† 1 γ€γ―η„‘θ¦–γ•γ‚ŒγΎγ™γ€‚γ‚γͺたが�?οΏ½θ£…γ—γŸγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ¨θ¨ˆοΏ½?οΏ½ι–’ζ•°γŒη΄”η²‹γ§γ‚γ‚Œγ°γ€γ“οΏ½?ζŒ™ε‹•γŒγƒ­γ‚Έγƒƒγ‚―γ«ε½±ιŸΏγ‚’δΈŽγˆγ‚‹γ“γ¨γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚γ—γ‹γ—γ€γ‚‚γ—ζ„ε›³γ›γšη΄”η²‹γ§γ―γͺい閒数にγͺγ£γ¦γ„γŸε ΄εˆγ―γ€γ“οΏ½?ζŒ™ε‹•γ«γ‚ˆγ£γ¦ι–“ι•γ„γ«ζ°—γ₯き、�?ζ­£γ™γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚

γŸγ¨γˆγ°γ€δ»₯δΈ‹οΏ½?計�?�閒数は、props γ¨γ—γ¦ε—γ‘ε–γ£γŸι…εˆ—οΏ½?ζ›Έγζ›γˆοΌˆγƒŸγƒ₯γƒΌγƒ†γƒΌγ‚·γƒ§γƒ³οΌ‰γ‚’γ—γ¦γ—γΎγ£γ¦γŠγ‚Šγ€η΄”η²‹γ§γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚

const visibleTodos = useMemo(() => {
// 🚩 Mistake: mutating a prop
todos.push({ id: 'last', text: 'Go for a walk!' });
const filtered = filterTodos(todos, tab);
return filtered;
}, [todos, tab]);

しかし、こ�?閒数は 2 εΊ¦ε‘Όγ³ε‡Ίγ•γ‚Œγ‚‹γŸγ‚γ€todo が 2 ε›žθΏ½εŠ γ•γ‚ŒγŸγ“γ¨γ«ζ°—γ₯γγ―γšγ§γ™γ€‚θ¨ˆοΏ½?οΏ½ι–’ζ•°γ―γ€ζ—’ε­˜οΏ½?γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’ε€‰ζ›΄γ—γ¦γ―γ„γ‘γΎγ›γ‚“γŒγ€θ¨ˆοΏ½?οΏ½δΈ­γ«δ½œζˆγ—γŸζ–°γ—γ„γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’ε€‰ζ›΄γ™γ‚‹γ“γ¨γ―ε•ι‘Œγ‚γ‚ŠγΎγ›γ‚“γ€‚γŸγ¨γˆγ°γ€filterTodos ι–’ζ•°γŒεΈΈγ«η•°γͺγ‚‹ι…εˆ—γ‚’θΏ”γ™ε ΄εˆγ―γ€γοΏ½?ι…εˆ—γ‚’ε€‰ζ›΄γ—γ¦γ‚‚ε•ι‘Œγ‚γ‚ŠγΎγ›γ‚“γ€‚

const visibleTodos = useMemo(() => {
const filtered = filterTodos(todos, tab);
// βœ… Correct: mutating an object you created during the calculation
filtered.push({ id: 'last', text: 'Go for a walk!' });
return filtered;
}, [todos, tab]);

純閒数に぀いて詳しくηŸ₯γ‚‹γ«γ―γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’η΄”η²‹γ«δΏγ€γ‚’ε‚η…§γ—γ¦γγ γ•γ„γ€‚

γΎγŸγ€γƒŸγƒ₯ーテーションγͺしでγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’ζ›΄ζ–°γ™γ‚‹ζ–Ήζ³•γ«γ€γ„γ¦γ―γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆοΏ½?ζ›΄ζ–°γ‚’γ€γƒŸγƒ₯ーテーションγͺγ—γ§ι…εˆ—γ‚’ζ›΄ζ–°γ™γ‚‹ζ–Ήζ³•γ«γ€γ„γ¦γ―ι…εˆ—οΏ½?更新を参照してください。


useMemo οΏ½?θΏ”γ‚Šε€€γŒγ€γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ§γ―γͺく undefined にγͺってしまう

δ»₯δΈ‹οΏ½?γ‚³γƒΌγƒ‰γ―γ†γΎγε‹•δ½œγ—γΎγ›γ‚“γ€‚

// πŸ”΄ You can't return an object from an arrow function with () => {
const searchOptions = useMemo(() => {
matchMode: 'whole-word',
text: text
}, [text]);

JavaScript では、() => { というコードでをロー閒数�?ζœ¬δ½“γ‚’ι–‹ε§‹γ™γ‚‹γŸγ‚γ€{ οΏ½?泒括弧はγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆοΏ½?一部にはγͺγ‚ŠγΎγ›γ‚“γ€‚γ—γŸγŒγ£γ¦γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ―θΏ”γ•γ‚Œγšγ€γƒŸγ‚Ήγ«γ€γͺγŒγ‚ŠγΎγ™γ€‚({ γ‚„ }) οΏ½?γ‚ˆγ†γ«δΈΈζ‹¬εΌ§γ‚’θΏ½εŠ γ™γ‚‹γ“γ¨γ§οΏ½?正できます。

// This works, but is easy for someone to break again
const searchOptions = useMemo(() => ({
matchMode: 'whole-word',
text: text
}), [text]);

γ—γ‹γ—γ€γ“γ‚Œγ§γ‚‚γΎγ ζ··δΉ±γ—γ‚„γ™γγ€θͺ°γ‹γŒδΈΈζ‹¬εΌ§γ‚’ε‰Šι™€γ—γ¦γ—γΎγ†γ¨η°‘ε˜γ«ε£Šγ‚Œγ¦γ—γΎγ„γΎγ™γ€‚

こ�?γƒŸγ‚Ήγ‚’ιΏγ‘γ‚‹γŸγ‚γ«γ€ζ˜Žη€Ίηš„γ« return 文を書きましょう。

// βœ… This works and is explicit
const searchOptions = useMemo(() => {
return {
matchMode: 'whole-word',
text: text
};
}, [text]);

γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒγƒ¬γƒ³γƒ€γƒΌγ•γ‚Œγ‚‹γŸγ³γ« useMemo ε†…οΏ½?ι–’ζ•°γŒε†οΏ½?οΏ½θ‘Œγ•γ‚Œγ‚‹

第 2 εΌ•ζ•°γ«δΎε­˜ι…εˆ—γ‚’ζŒ‡οΏ½?�しているか璺θͺγ—てください!

δΎε­˜ι…εˆ—γ‚’εΏ˜γ‚Œγ‚‹γ¨γ€useMemo γ―ζ―Žε›žθ¨ˆοΏ½?�を再�?οΏ½θ‘Œγ—γ¦γ—γΎγ„γΎγ™γ€‚

function TodoList({ todos, tab }) {
// πŸ”΄ Recalculates every time: no dependency array
const visibleTodos = useMemo(() => filterTodos(todos, tab));
// ...

第 2 εΌ•ζ•°γ«δΎε­˜ι…εˆ—γ‚’ζΈ‘γ—γŸοΏ½?ζ­£η‰ˆγ―δ»₯δΈ‹οΏ½?ι€šγ‚Šγ§γ™γ€‚

function TodoList({ todos, tab }) {
// βœ… Does not recalculate unnecessarily
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
// ...

γ“γ‚Œγ§θ§£ζ±Ίγ—γͺγ„ε ΄εˆγ―γ€ε°‘γͺくとも 1 ぀�?δΎε­˜ε€€γŒε‰ε›žοΏ½?レンダーと異γͺγ£γ¦γ„γ‚‹γ“γ¨γŒε•ι‘Œγ§γ™γ€‚ζ‰‹ε‹•γ§δΎε­˜ε€€γ‚’γ‚³γƒ³γ‚½γƒΌγƒ«γ«ε‡ΊεŠ›γ—γ¦γ€γƒ‡γƒγƒƒγ‚°γ™γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚

const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
console.log([todos, tab]);

γ‚³γƒ³γ‚½γƒΌγƒ«δΈŠγ§γ€εˆ₯γ€…οΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ«γ‚ˆγ£γ¦θ‘¨η€Ίγ•γ‚ŒγŸ 2 ぀�?ι…εˆ—γ‚’ιΈγ³γΎγ™γ€‚γγ‚Œγžγ‚Œγ«γ€γ„γ¦γ€ι…εˆ—γ‚’ε³γ‚―γƒͺγƒƒγ‚―γ—γ€β€œStore as a global variableοΌˆγ‚°γƒ­γƒΌγƒγƒ«ε€‰ζ•°γ¨γ—γ¦δΏε­˜οΌ‰β€ γ‚’ιΈζŠžγ™γ‚‹γ“γ¨γ§γ€ι…εˆ—γ‚’δΏε­˜γ™γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚1 ε›žοΏ½?γ«δΏε­˜γ—γŸι…εˆ—γŒ temp1、2 ε›žοΏ½?γ«δΏε­˜γ—γŸι…εˆ—γŒ temp2 γ¨γ—γ¦δΏε­˜γ•γ‚ŒγŸγ¨γ™γ‚‹γ¨γ€γƒ–γƒ©γ‚¦γ‚ΆοΏ½?コンソールを使用して、丑方�?ι…εˆ—οΏ½?ε„δΎε­˜ε€€γŒεŒγ˜γ‹γ©γ†γ‹γ‚’η’Ίθͺγ§γγΎγ™γ€‚

Object.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays?
Object.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays?
Object.is(temp1[2], temp2[2]); // ... and so on for every dependency ...

γƒ‘γƒ’εŒ–γ‚’ε¦¨γ’γ¦γ„γ‚‹δΎε­˜ε€€γ‚’θ¦‹γ€γ‘γŸγ‚‰γ€γοΏ½?δΎε­˜ε€€γ‚’ε‰Šι™€γ™γ‚‹ζ–Ήζ³•γ‚’ζŽ’γ™γ‹γ€γοΏ½?δΎε­˜ε€€γ‚‚γƒ‘γƒ’εŒ–γ—γΎγ—γ‚‡γ†γ€‚


ループ内�?γƒͺγ‚ΉγƒˆοΏ½?各項�?に぀いて useMemo γ‚’ε‘Όγ³ε‡Ίγ—γŸγ„γŒγ€η¦ζ­’γ•γ‚Œγ¦γ„γ‚‹

Chart γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒ memo γ§γƒ©γƒƒγƒ—γ•γ‚Œγ¦γ„γ‚‹γ¨γ—γΎγ™γ€‚ReportList γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒε†γƒ¬γƒ³γƒ€γƒΌγ•γ‚ŒγŸε ΄εˆγ§γ‚‚γ€γƒͺγ‚Ήγƒˆε†…οΏ½?各 Chart οΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ―γ‚Ήγ‚­γƒƒγƒ—γ—γŸγ„γ§γ™γ€‚γ¨γ“γ‚γŒγ€δ»₯δΈ‹οΏ½?γ‚ˆγ†γ«γƒ«γƒΌγƒ—ε†…γ§ useMemo を呼び出すことはできません。

function ReportList({ items }) {
return (
<article>
{items.map(item => {
// πŸ”΄ You can't call useMemo in a loop like this:
const data = useMemo(() => calculateReport(item), [item]);
return (
<figure key={item.id}>
<Chart data={data} />
</figure>
);
})}
</article>
);
}

そ�?ε ΄εˆγ―γ€ε„γ‚’γ‚€γƒ†γƒ γ‚’γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«εˆ‡γ‚Šε‡Ίγ—γ€γ‚’γ‚€γƒ†γƒ γ”γ¨γ«γƒ‡γƒΌγ‚Ώγ‚’γƒ‘γƒ’εŒ–γ—γΎγ™γ€‚

function ReportList({ items }) {
return (
<article>
{items.map(item =>
<Report key={item.id} item={item} />
)}
</article>
);
}

function Report({ item }) {
// βœ… Call useMemo at the top level:
const data = useMemo(() => calculateReport(item), [item]);
return (
<figure>
<Chart data={data} />
</figure>
);
}

あるいは、useMemo γ‚’ε‰Šι™€γ—γ€Report θ‡ͺ体を memo でラップすることでも解決できます。item γŒε€‰εŒ–γ—γͺγ„ε ΄εˆγ―γ€Report οΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ―γ‚Ήγ‚­γƒƒγƒ—γ•γ‚Œγ€Chart οΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ‚‚γ‚Ήγ‚­γƒƒγƒ—γ•γ‚ŒγΎγ™γ€‚

function ReportList({ items }) {
// ...
}

const Report = memo(function Report({ item }) {
const data = calculateReport(item);
return (
<figure>
<Chart data={data} />
</figure>
);
});