← Назад к вопросам
Для чего нужен второй callback в useMemo?
2.3 Middle🔥 211 комментариев
#React#State Management#Оптимизация и производительность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Для чего нужен второй callback в useMemo?
В React хуке useMemo второй параметр — это массив зависимостей (dependency array). Это критически важный механизм для оптимизации и контроля когда мемоизированное значение пересчитывается.
Синтаксис
const memoizedValue = useMemo(
() => computeExpensiveValue(a, b), // первый аргумент: функция
[a, b] // второй аргумент: зависимости
);
Как работает массив зависимостей
Re act сравнивает предыдущий массив зависимостей с новым:
const [count, setCount] = useState(0);
const [name, setName] = useState('Alice');
const expensive = useMemo(() => {
console.log('Computing...');
return count * 2;
}, [count]); // Зависит только от count!
// Когда count изменился → пересчитай
// Когда name изменился → НЕ пересчитывай (count же не изменился)
Варианты массива зависимостей
1. Пустой массив [] — считать один раз
const expensiveData = useMemo(() => {
console.log('Loading data...');
return fetchData(); // выполнится один раз при монтировании
}, []);
Это полезно для:
- Инициализации данных
- Создания объектов, которые никогда не меняются
- Начальной загрузки API
2. С зависимостями [a, b] — считать если они меняются
const total = useMemo(() => {
console.log('Computing total...');
return a + b;
}, [a, b]);
// Если a или b изменилась → пересчитай
// Если другие переменные изменились → не трогай
3. Без второго параметра (ПЛОХО!) — считать на каждый рендер
// ❌ Плохо: это бесполезно, вычисляет каждый раз
const expensive = useMemo(() => {
return computeExpensive(a);
});
// Такой useMemo ничем не помогает!
Практические примеры
Пример 1: Фильтрация большого списка
const FilteredList = ({ items, filter }) => {
// Пересчитываем фильтрованный список только если items или filter изменился
const filteredItems = useMemo(() => {
console.log('Filtering...');
return items.filter(item => item.name.includes(filter));
}, [items, filter]);
return (
<ul>
{filteredItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};
// При рендере родителя, если items и filter не изменились,
// filteredItems останется прежним → ListItem компоненты не перерендерятся
Пример 2: Обработка данных для графика
const DataChart = ({ rawData, metric }) => {
// Обрабатываем данные только если rawData или metric изменился
const processedData = useMemo(() => {
console.time('Processing');
const processed = rawData
.filter(d => d.metric === metric)
.map(d => ({ ...d, value: d.value * 1.1 }))
.sort((a, b) => b.value - a.value);
console.timeEnd('Processing');
return processed;
}, [rawData, metric]);
return <BarChart data={processedData} />;
};
Пример 3: Создание объектов для сравнения
const Component = ({ userId, userName }) => {
// Создаём объект пользователя один раз (пока userId и userName не изменились)
const user = useMemo(() => {
return { id: userId, name: userName };
}, [userId, userName]);
// Можем безопасно передать в другой компонент
return <UserCard user={user} />;
};
Без useMemo каждый рендер создал бы новый объект:
const user = { id: userId, name: userName }; // ❌ Новый объект каждый раз!
// UserCard будет перерендеряться даже если данные не изменились
Ошибки при использовании зависимостей
Ошибка 1: Забыть добавить зависимость
const value = useMemo(() => {
return a + b; // используем a и b
}, [a]); // ❌ Забыли b!
// Когда b изменится, React не пересчитает значение
// value остаётся старым → баг!
Ошибка 2: Добавить всё подряд
const [a, setA] = useState(0);
const [b, setB] = useState(0);
const [c, setC] = useState(0);
const result = useMemo(() => {
return a + b;
}, [a, b, c]); // ❌ c не используется!
// Когда c изменится, React пересчитает a + b (бесполезно)
Советы по использованию
1. Используй ESLint плагин
npm install --save-dev eslint-plugin-react-hooks
Он подскажет какие зависимости забыли:
const value = useMemo(() => {
return a + b;
}, []); // ⚠️ ESLint предупредит!
2. Используй useMemo для дорогих вычислений
// Хорошие кейсы для useMemo:
// - Фильтрация большого списка
// - Сортировка (O(n log n))
// - Транформация данных
// - Создание объектов для memo компонентов
// Плохие кейсы:
// - Простые арифметические операции
// - Доступ к свойствам объекта
// - Строковые операции
3. Комбо: useMemo + React.memo
const Parent = () => {
const data = useMemo(() => {
return { value: expensiveCalculation() };
}, []);
return <Child data={data} />;
};
const Child = React.memo(({ data }: Props) => {
return <div>{data.value}</div>;
});
// Теперь Child не перерендерится, если data не изменился
Производительность
// До useMemo: всё вычисляется каждый раз
const App = () => {
const [count, setCount] = useState(0);
const filtered = items.filter(...); // O(n) на каждый рендер!
return (
<>
<button onClick={() => setCount(count + 1)}>Increment</button>
<List items={filtered} />
</>
);
};
// После useMemo: вычисляется только при необходимости
const App = () => {
const [count, setCount] = useState(0);
const filtered = useMemo(
() => items.filter(...),
[items]
);
return (
<>
<button onClick={() => setCount(count + 1)}>Increment</button>
<List items={filtered} />
</>
);
};
Резюме
Массив зависимостей — это инструкция React'у:
- Когда пересчитывать мемоизированное значение
- Какие переменные влияют на результат
- Когда результат можно переиспользовать
Правило:
- Если ничего не меняется →
[] - Если что-то меняется → добавь в
[...] - Включи в массив все переменные, используемые в функции
Без правильного массива зависимостей useMemo становится бесполезным или даже вредным.