react18官方文档
useMemo
useMemo 是一个 React Hook,它在每次重新渲染的时候能够缓存计算的结果。
const cachedValue = useMemo(calculateValue, dependencies)
// 1. 跳过代价昂贵的重新计算
import React, { useState, useMemo } from 'react';
function ExpensiveCalculationExample() {
const [numbers, setNumbers] = useState([1, 2, 3, 4, 5]);
const [count, setCount] = useState(0);
// 使用 useMemo 缓存计算结果
const expensiveSum = useMemo(() => {
console.log('Computing expensive sum...');
return numbers.reduce((acc, curr) => {
// 模拟耗时计算
for(let i = 0; i < 1000000; i++) {}
return acc + curr;
}, 0);
}, [numbers]); // 只在 numbers 改变时重新计算
return (
<div>
<p>Sum: {expensiveSum}</p>
<button onClick={() => setCount(c => c + 1)}>
Increment Count: {count}
</button>
<button onClick={() => setNumbers([...numbers, numbers.length + 1])}>
Add Number
</button>
</div>
);
}
// 2. 跳过组件的重新渲染
function MemoizedChildComponent({ data }) {
console.log('Child component rendered');
return <div>{data.value}</div>;
}
function ParentComponent() {
const [count, setCount] = useState(0);
const [value, setValue] = useState('Hello');
// 使用 useMemo 缓存对象
const memoizedData = useMemo(() => ({
value: value
}), [value]);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
<MemoizedChildComponent data={memoizedData} />
</div>
);
}
// 3. 防止过于频繁地触发 Effect
function SearchComponent() {
const [searchTerm, setSearchTerm] = useState('');
const [results, setResults] = useState([]);
const [filters, setFilters] = useState({ category: 'all', price: 'any' });
// 使用 useMemo 缓存搜索条件
const searchConfig = useMemo(() => ({
term: searchTerm,
filters: filters
}), [searchTerm, filters]);
useEffect(() => {
const fetchResults = async () => {
const response = await fetch(
`/api/search?term=${searchConfig.term}&filters=${JSON.stringify(searchConfig.filters)}`
);
const data = await response.json();
setResults(data);
};
fetchResults();
}, [searchConfig]); // 只在搜索配置改变时触发
return (
<div>
<input
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
/>
<select
value={filters.category}
onChange={e => setFilters({...filters, category: e.target.value})}
>
<option value="all">All</option>
<option value="electronics">Electronics</option>
<option value="books">Books</option>
</select>
<ul>
{results.map(result => (
<li key={result.id}>{result.title}</li>
))}
</ul>
</div>
);
}
// 4. 记忆另一个 Hook 的依赖
function DataVisualization() {
const [data, setData] = useState([]);
const [viewConfig, setViewConfig] = useState({ type: 'bar', color: 'blue' });
// 使用 useMemo 处理数据转换
const processedData = useMemo(() => {
return data.map(item => ({
...item,
value: item.value * 2,
color: viewConfig.color
}));
}, [data, viewConfig.color]);
// 使用处理后的数据作为自定义 Hook 的依赖
useChartEffect(processedData, viewConfig.type);
return (
<div>
<button onClick={() => setViewConfig({
...viewConfig,
type: viewConfig.type === 'bar' ? 'line' : 'bar'
})}>
Toggle Chart Type
</button>
<div id="chart-container">
{/* Chart rendering logic */}
</div>
</div>
);
}
// 5. 记忆一个函数
function TodoList() {
const [todos, setTodos] = useState([]);
const [filter, setFilter] = useState('all');
// 使用 useMemo 缓存过滤函数
const getFilteredTodos = useMemo(() => {
return (todoList) => {
console.log('Filtering todos...');
switch(filter) {
case 'completed':
return todoList.filter(todo => todo.completed);
case 'active':
return todoList.filter(todo => !todo.completed);
default:
return todoList;
}
};
}, [filter]);
// 使用记忆的函数
const filteredTodos = getFilteredTodos(todos);
const addTodo = (text) => {
setTodos([...todos, {
id: Date.now(),
text,
completed: false
}]);
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
};
return (
<div>
<div>
<button onClick={() => setFilter('all')}>All</button>
<button onClick={() => setFilter('active')}>Active</button>
<button onClick={() => setFilter('completed')}>Completed</button>
</div>
<ul>
{filteredTodos.map(todo => (
<li
key={todo.id}
onClick={() => toggleTodo(todo.id)}
style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}
>
{todo.text}
</li>
))}
</ul>
<AddTodoForm onAdd={addTodo} />
</div>
);
}
// 辅助组件:添加待办事项表单
function AddTodoForm({ onAdd }) {
const [text, setText] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (!text.trim()) return;
onAdd(text);
setText('');
};
return (
<form onSubmit={handleSubmit}>
<input
value={text}
onChange={e => setText(e.target.value)}
placeholder="Add new todo"
/>
<button type="submit">Add</button>
</form>
);
}
// 自定义 Hook 示例
function useChartEffect(data, chartType) {
useEffect(() => {
// 模拟图表渲染逻辑
console.log(`Rendering ${chartType} chart with data:`, data);
// 清理函数
return () => {
console.log('Cleaning up chart');
};
}, [data, chartType]);
}
// 完整应用示例
function App() {
return (
<div>
<h2>Expensive Calculation Example</h2>
<ExpensiveCalculationExample />
<h2>Component Re-rendering Example</h2>
<ParentComponent />
<h2>Search Component Example</h2>
<SearchComponent />
<h2>Data Visualization Example</h2>
<DataVisualization />
<h2>Todo List Example</h2>
<TodoList />
</div>
);
}
export default App;