function TodoList({ todos, tab }) {
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
);
// ...
}
function TodoList({ todos, tab, theme }) {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
// ...
}
비용이 많이 드는 계산인지는 어떻게 알 수 있나요?
console.time('filter array');
const visibleTodos = filterTodos(todos, tab);
console.timeEnd('filter array');
console.time('filter array');
const visibleTodos = useMemo(() => {
return filterTodos(todos, tab);
// todos 및 tab이 모두 변경되지 않으면 건너뜁니다
}, [todos, tab]);
console.timeEnd('filter array');
모든 곳에 useMemo를 추가해야 하나요?
useMemo와 값을 직접 계산하는 것의 차이점
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = filterTodos(todos, tab);
return (
<div className={theme}>
<ul>
<p><b>Note: <code>filterTodos</code> is artificially slowed down!</b></p>
{visibleTodos.map(todo => (
<li key={todo.id}>
{todo.completed ?
<s>{todo.text}</s> :
todo.text
}
</li>
))}
</ul>
</div>
);
}
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
);
export default function TodoList({ todos, tab, theme }) {
// ...
return (
<div className={theme}>
<List items={visibleTodos} />
</div>
);
}
import { memo } from 'react';
const List = memo(function List({ items }) {
// ...
});
export default function TodoList({ todos, tab, theme }) {
// theme가 변경될 때마다 매번 다른 배열이 됩니다...
const visibleTodos = filterTodos(todos, tab);
return (
<div className={theme}>
{/* ... List의 prop은 절대로 같을 수 없으므로, 매번 리렌더링할 것입니다 */}
<List items={visibleTodos} />
</div>
);
}
export default function TodoList({ todos, tab, theme }) {
// 리렌더링 사이에 계산 결과를 캐싱하도록 합니다...
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
// ...따라서 여기의 의존성이 변경되지 않는다면 ...
);
return (
<div className={theme}>
{/* ...List는 같은 props를 전달받게 되어 리렌더링을 건너뛸 수 있게 됩니다 */}
<List items={visibleTodos} />
</div>
);
}
개별 JSX 노드 메모화
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>
);
}
리렌더링을 건너뛰는 것과 항상 리렌더링하는 것의 차이점
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>
);
}
const List = memo(function List({ items }) {
console.log('[ARTIFICIALLY SLOW] Rendering <List /> with ' + items.length + ' items');
let startTime = performance.now();
while (performance.now() - startTime < 500) {
// Do nothing for 500 ms to emulate extremely slow code
}
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.completed ?
<s>{item.text}</s> :
item.text
}
</li>
))}
</ul>
);
});
function Dropdown({ allItems, text }) {
const searchOptions = { matchMode: 'whole-word', text };
const visibleItems = useMemo(() => {
return searchItems(allItems, searchOptions);
}, [allItems, searchOptions]);
// 🚩 주의: 컴포넌트 내부에서 생성한 객체 의존성
// ...
function Dropdown({ allItems, text }) {
const searchOptions = useMemo(() => {
return { matchMode: 'whole-word', text };
}, [text]); // ✅ text 변경시에만 변경됨
const visibleItems = useMemo(() => {
return searchItems(allItems, searchOptions);
}, [allItems, searchOptions]); //✅ allItems 또는 searchOptions 변경시에만 변경됨
// ...
function Dropdown({ allItems, text }) {
const visibleItems = useMemo(() => {
const searchOptions = { matchMode: 'whole-word', text };
return searchItems(allItems, searchOptions);
}, [allItems, text]); // ✅ allItems 또는 text 변경시에만 변경됨
// ...
export default function ProductPage({ productId, referrer }) {
function handleSubmit(orderDetails) {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
}
return <Form onSubmit={handleSubmit} />;
}
export default function Page({ productId, referrer }) {
const handleSubmit = useMemo(() => {
return (orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
};
}, [productId, referrer]);
return <Form onSubmit={handleSubmit} />;
}
export default function Page({ productId, referrer }) {
const handleSubmit = useCallback((orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
}, [productId, referrer]);
return <Form onSubmit={handleSubmit} />;
}
const visibleTodos = useMemo(() => {
// 🚩 Mistake: mutating a prop
// 🚩 실수: prop 변이
todos.push({ id: 'last', text: 'Go for a walk!' });
const filtered = filterTodos(todos, tab);
return filtered;
}, [todos, tab]);
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]);
// 🔴 화살표 함수에서 () => { 만으로는 객체를 리턴할 수 없습니다.
const searchOptions = useMemo(() => {
matchMode: 'whole-word',
text: text
}, [text]);
// 이 코드는 작동하지만, 누군가에 의해 다시 꺠지기 쉽습니다
const searchOptions = useMemo(() => ({
matchMode: 'whole-word',
text: text
}), [text]);
// ✅ 이 코드는 작동하며 명시적입니다
const searchOptions = useMemo(() => {
return {
matchMode: 'whole-word',
text: text
};
}, [text]);
function TodoList({ todos, tab }) {
// 🔴 매 번 재계산: 의존성 배열이 없음
const visibleTodos = useMemo(() => filterTodos(todos, tab));
// ...
function TodoList({ todos, tab }) {
// ✅ 불필요하게 재계산하지 않음
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
// ...
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
console.log([todos, tab]);
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 ...
// ... 나머지 모든 의존성에 대해 반복 ...
function ReportList({ items }) {
return (
<article>
{items.map(item =>
<Report key={item.id} item={item} />
)}
</article>
);
}
function Report({ item }) {
// ✅ Call useMemo at the top level:
// ✅ useMemo는 컴포넌트 최상단에서 호출하세요:
const data = useMemo(() => calculateReport(item), [item]);
return (
<figure>
<Chart data={data} />
</figure>
);
}
function ReportList({ items }) {
// ...
}
const Report = memo(function Report({ item }) {
const data = calculateReport(item);
return (
<figure>
<Chart data={data} />
</figure>
);
});