Как React Hooks сравнивают deps между собой?
Хуки
Хуки (useEffect, useMemo, useCallback) сравнивают deps поэлементно и поверхностно: каждый элемент нового массива сопоставляется с элементом той же позиции из прошлого рендера через Object.is.
Алгоритм
// упрощённо — то, что делает React:
function areDepsEqual(next, prev) {
if (prev === null) return false; // первый рендер — «изменилось»
for (let i = 0; i < next.length; i++) {
if (!Object.is(next[i], prev[i])) return false;
}
return true;
}- Массивы сравниваются по позициям (поэтому длина и порядок deps должны быть стабильны — литерал в месте вызова).
- Хоть один элемент отличается — эффект перезапускается / значение пересчитывается / функция пересоздаётся.
Что такое Object.is
Почти строгое равенство ===, с двумя отличиями: Object.is(NaN, NaN) === true и Object.is(0, -0) === false. Главное:
- примитивы — по значению:
'a' === 'a',5 === 5; - объекты/массивы/функции — по ссылке:
{} !== {}даже при одинаковом содержимом.
Следствия
const options = { page }; // новая ссылка каждый рендер
useEffect(() => load(options), [options]); // «изменилось» всегда!
const user = useMemo(() => ({ id }), [id]); // стабильная ссылка
useEffect(() => track(user), [user]); // перезапуск только при смене id- Глубокого сравнения нет никогда (дорого); мутации не видны (ссылка та же).
- Нестабильные объекты/функции в deps → бесконечные перезапуски; лечится useMemo/useCallback, примитивами или переносом в эффект.
Коротко для собеседования
Deps сравниваются поэлементно через Object.is (по сути ===, но NaN равен NaN): примитивы по значению, объекты/функции по ссылке; глубокого сравнения нет. Один отличающийся элемент — хук «перезапускается». Отсюда требования: стабильные ссылки в deps и постоянный состав массива.