Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как React Hooks сравнивают deps между собой?
Механизм сравнения dependencies
React использует Object.is() для сравнения каждой зависимости в массиве dependencies. Это отличается от обычного оператора ===, хотя в большинстве случаев результат одинаков.
Object.is() - алгоритм сравнения
// Object.is() сравнивает значения
Object.is(5, 5) // true
Object.is("hello", "hello") // true
Object.is(true, true) // true
Object.is(NaN, NaN) // true - отличие от ===!
Object.is(+0, -0) // false - отличие от ===!
// Для объектов - сравнение по ссылке (как ===)
const obj1 = { name: "Алиса" };
const obj2 = { name: "Алиса" };
Object.is(obj1, obj2) // false - разные ссылки
Object.is(obj1, obj1) // true - одна ссылка
Как это работает в useEffect
function Component() {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
useEffect(() => {
console.log("Effect запустился");
// Что-то делаем
}, [count, text]); // Dependency array
// При каждом рендере React:
// 1. Сохраняет старый deps: [0, ""]
// 2. Вычисляет новый deps: [count, text]
// 3. Сравнивает каждый элемент с Object.is()
// 4. Если хоть один отличается - запускает эффект
}
Поэтапное сравнение
// Шаг 1: Первый рендер - эффект всегда запускается
useEffect(() => {
console.log("Инициализация");
}, []);
// Шаг 2: Второй рендер
const oldDeps = [0];
const newDeps = [0];
Object.is(oldDeps[0], newDeps[0]) // true - эффект НЕ запустится
// Шаг 3: Третий рендер (count изменился)
const oldDeps = [0];
const newDeps = [1];
Object.is(oldDeps[0], newDeps[0]) // false - эффект запустится
Проблема с объектами в dependencies
Поскольку объекты сравниваются по ссылке, создание нового объекта в каждом рендере приведёт к повторному запуску эффекта:
function UserProfile() {
// ПРОБЛЕМА: новый объект при каждом рендере
const user = { id: 1, name: "Алиса" };
useEffect(() => {
console.log("Загружаем данные для", user.name);
// Запрос на сервер
}, [user]); // user всегда новый - эффект запустится бесконечно!
}
// РЕШЕНИЕ 1: вынеси объект вне компонента
const user = { id: 1, name: "Алиса" };
function UserProfile() {
useEffect(() => {
console.log("Загружаем", user.name);
}, [user]); // Одна и та же ссылка
}
// РЕШЕНИЕ 2: используй примитивные значения
function UserProfile({ userId }: { userId: number }) {
useEffect(() => {
console.log("Загружаем юзера", userId);
}, [userId]); // Сравнивается по значению
}
// РЕШЕНИЕ 3: используй useMemo
function UserProfile() {
const user = useMemo(
() => ({ id: 1, name: "Алиса" }),
[] // Зависимости для самого useMemo
);
useEffect(() => {
console.log("Загружаем", user.name);
}, [user]); // Одна и та же ссылка (создана один раз)
}
Аналогично для useCallback и useMemo
function Component() {
const [count, setCount] = useState(0);
// Зависимости сравниваются так же
const memoizedValue = useMemo(
() => expensiveCalculation(count),
[count] // Object.is сравнивает count
);
const memoizedCallback = useCallback(
() => {
console.log(count);
},
[count] // Object.is сравнивает count
);
return <button onClick={memoizedCallback}>{count}</button>;
}
Практическая таблица сравнения
| Тип | Сравнение | Пример |
|---|---|---|
| Number | По значению | 1 === 1 -> true |
| String | По значению | "hello" === "hello" -> true |
| Boolean | По значению | true === true -> true |
| Object | По ссылке | {} === {} -> false |
| Array | По ссылке | [] === [] -> false |
| Function | По ссылке | (() => {}) === (() => {}) -> false |
| NaN | Специально | Object.is(NaN, NaN) -> true |
Типичная ошибка
// НЕПРАВИЛЬНО - бесконечный цикл
function Search({ query }) {
const [results, setResults] = useState([]);
useEffect(() => {
setResults([{ title: "Результат" }]); // Новый массив
}, [results]); // results меняется -> эффект запускается
// -> setResults создаёт новый массив
// -> эффект запускается снова...
}
// ПРАВИЛЬНО
function Search({ query }) {
const [results, setResults] = useState([]);
useEffect(() => {
// Запрос на сервер
fetch(`/api/search?q=${query}`)
.then(r => r.json())
.then(setResults);
}, [query]); // Зависимость от query, не от results
}
Заключение
React сравнивает dependencies поэлементно с Object.is():
- Примитивы сравниваются по значению
- Объекты и массивы сравниваются по ссылке
- Если хоть одна зависимость изменилась - эффект запускается
- Используй useMemo для объектов и useCallback для функций, если они зависимости
- Избегай создания новых объектов в каждом рендере в deps array