← Назад к вопросам
Выполнится ли перерисовка если в useState изменить значение на такое же
2.0 Middle🔥 301 комментариев
#React#Оптимизация и производительность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Перерисовка при установке того же значения в useState
Короткий ответ: Нет, перерисовка не происходит, если значение одинаковое. React использует Object.is() для сравнения.
Как работает сравнение в React 18+
const [count, setCount] = useState(0);
// Внутри React
setCount(0); // Object.is(0, 0) === true
// Результат: перерисовка НЕ происходит
setCount(1); // Object.is(0, 1) === false
// Результат: перерисовка ПРОИСХОДИТ
Детальный механизм
React использует Object.is() для сравнения
// Object.is() — это строгое сравнение
Object.is(0, 0); // true
Object.is('hello', 'hello'); // true
Object.is(false, false); // true
// Отличие от ===
Object.is(NaN, NaN); // true (отличие от ===)
Object.is(-0, +0); // false (отличие от ===)
Object.is([], []); // false (разные ссылки)
Примеры
Пример 1: Примитивы (числа, строки)
function Counter() {
const [count, setCount] = useState(0);
const handleSame = () => {
console.log('Setting same value: 0');
setCount(0); // Object.is(0, 0) === true
// Перерисовка НЕ происходит
};
const handleDifferent = () => {
console.log('Setting different value: 1');
setCount(1); // Object.is(0, 1) === false
// Перерисовка ПРОИСХОДИТ
};
console.log('Render with count:', count);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleSame}>Set same (0)</button>
<button onClick={handleDifferent}>Set different (1)</button>
</div>
);
}
// Вывод консоли:
// "Render with count: 0"
// Нажимаем "Set same (0)":
// "Setting same value: 0"
// (ничего не печатается, перерисовки нет)
// Нажимаем "Set different (1)":
// "Setting different value: 1"
// "Render with count: 1"
Пример 2: Объекты (перерисовка всегда происходит)
function User() {
const [user, setUser] = useState({ id: 1, name: 'Alice' });
const handleSameUser = () => {
// Object.is({ id: 1, name: 'Alice' }, { id: 1, name: 'Alice' }) === false
// Разные ссылки в памяти!
setUser({ id: 1, name: 'Alice' });
// Перерисовка ПРОИСХОДИТ (новый объект)
};
const handleSameReference = () => {
// Передаём ту же ссылку
const sameUser = user;
setUser(sameUser);
// Object.is(user, sameUser) === true
// Перерисовка НЕ происходит (та же ссылка)
};
console.log('Render with user:', user.name);
return (
<div>
<p>User: {user.name}</p>
<button onClick={handleSameUser}>Set same object (new)</button>
<button onClick={handleSameReference}>Set same reference</button>
</div>
);
}
// Вывод консоли:
// "Render with user: Alice"
// Нажимаем "Set same object (new)":
// "Render with user: Alice" (перерисовка произошла!)
// Нажимаем "Set same reference":
// (ничего не печатается, перерисовки нет)
Почему это работает так?
Реакт оптимизирует производительность, избегая ненужных перерисовок:
1. setCount(0) -> 0 === 0? ДА -> Пропусти перерисовку
2. setCount(1) -> 1 === 0? НЕТ -> Выполни перерисовку
Это экономит:
- Время процессора — не пересчитываем виртуальный DOM
- Память — не создаём новые объекты
- Электричество — батарея держит дольше
Пример 3: Boolean значения
function Toggle() {
const [isOpen, setIsOpen] = useState(false);
const handleSame = () => {
setIsOpen(false); // Object.is(false, false) === true
// Перерисовка НЕ происходит
};
const handleToggle = () => {
setIsOpen(true); // Object.is(false, true) === false
// Перерисовка ПРОИСХОДИТ
};
console.log('Rendering with isOpen:', isOpen);
return (
<div>
{isOpen && <p>Content is visible</p>}
<button onClick={handleSame}>Close (same value)</button>
<button onClick={handleToggle}>Open</button>
</div>
);
}
Пример 4: null и undefined
function Example() {
const [value, setValue] = useState(null);
const handleNull = () => {
setValue(null); // Object.is(null, null) === true
// Перерисовка НЕ происходит
};
const handleUndefined = () => {
setValue(undefined); // Object.is(null, undefined) === false
// Перерисовка ПРОИСХОДИТ
};
console.log('Rendering with value:', value);
return (
<div>
<button onClick={handleNull}>Set null</button>
<button onClick={handleUndefined}>Set undefined</button>
</div>
);
}
Пример 5: NaN (особый случай)
function Example() {
const [value, setValue] = useState(NaN);
const handleNaN = () => {
setValue(NaN); // Object.is(NaN, NaN) === true (иначе чем обычно!)
// Перерисовка НЕ происходит
};
const handleNumber = () => {
setValue(0); // Object.is(NaN, 0) === false
// Перерисовка ПРОИСХОДИТ
};
console.log('Rendering with value:', value);
return (
<div>
<button onClick={handleNaN}>Set NaN</button>
<button onClick={handleNumber}>Set 0</button>
</div>
);
}
// Интересно: Object.is(NaN, NaN) === true
// Но в JavaScript обычно NaN !== NaN
// React'ом используется Object.is(), поэтому NaN === NaN
Более сложный пример: Функция обновления
function Counter() {
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(prevCount => {
console.log(`Previous: ${prevCount}, Setting: ${prevCount + 1}`);
return prevCount + 1;
});
// Если prevCount был 0, то вернёт 1
// Object.is(0, 1) === false
// Перерисовка ПРОИСХОДИТ
};
const handleSetZero = () => {
setCount(prevCount => {
const newValue = prevCount === 0 ? 0 : 0; // Всегда 0
console.log(`Previous: ${prevCount}, Setting: ${newValue}`);
return newValue;
});
// Если count был 0:
// Object.is(0, 0) === true
// Перерисовка НЕ происходит
//
// Если count был 5:
// Object.is(5, 0) === false
// Перерисовка ПРОИСХОДИТ
};
console.log('Render with count:', count);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>Increment</button>
<button onClick={handleSetZero}>Set Zero</button>
</div>
);
}
Как это работает внутри React
// Упрощённый код React
function setState(newValue) {
// 1. Сравниваем старое и новое значение
if (Object.is(state, newValue)) {
// 2. Если одинаковые, пропускаем
return; // Перерисовка НЕ происходит
}
// 3. Если разные, обновляем
state = newValue;
// 4. Запускаем перерисовку
scheduleRender();
}
Best Practices
1. Не создавай новые объекты каждый раз
// ПЛОХО: новый объект каждый раз
const handleClick = () => {
setUser({ id: 1, name: 'Alice' });
// Перерисовка ВСЕГДА происходит, даже если значение то же
};
// ХОРОШО: обновляй свойства
const handleClick = () => {
setUser(prev => ({ ...prev, name: 'Bob' }));
// Перерисовка происходит только если name изменилась
};
2. Используй useMemo для сложных объектов
function Component() {
const config = useMemo(() => ({
timeout: 5000,
retries: 3
}), []); // создаётся один раз
setConfig(config);
// Object.is(config, config) === true
// Перерисовка НЕ происходит
}
3. Для массивов используй правильные методы
// ПЛОХО: создаёт новый массив каждый раз
setItems([...items, newItem]);
// Перерисовка ПРОИСХОДИТ (новый массив)
// ХОРОШО: но смотри контекст
setItems(prev => [...prev, newItem]);
// Перерисовка ПРОИСХОДИТ (новый массив, это нормально)
Тонкости
1. Что происходит с обновлением состояния?
setCount(0); // count was 0
// Object.is(0, 0) === true
// Перерисовка НЕ происходит ❌
setCount(count + 1); // count was 0
// Object.is(0, 1) === false
// Перерисовка ПРОИСХОДИТ ✓
2. Batching (батчинг обновлений)
function handleClick() {
setCount(1); // Отложит перерисовку
setName('Alice'); // Отложит перерисовку
// React выполнит одну перерисовку для обоих состояний
// Это называется "batching" (батчинг)
}
Реальный пример: Форма
function Form() {
const [formData, setFormData] = useState({ name: '', email: '' });
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => {
const updated = { ...prev, [name]: value };
// React сравнит: Object.is(prev, updated)
// Результат: false (новый объект)
// Перерисовка ПРОИСХОДИТ (это нужно)
return updated;
});
};
const handleReset = () => {
setFormData({ name: '', email: '' });
// Если уже пусто: Object.is({}, {})
// Результат: false (разные ссылки)
// Перерисовка ПРОИСХОДИТ (хотя значения одинаковые)
};
return (
<form>
<input name="name" value={formData.name} onChange={handleChange} />
<input name="email" value={formData.email} onChange={handleChange} />
<button onClick={handleReset}>Reset</button>
</form>
);
}
Итог
Перерисовка происходит только если:
Object.is(oldValue, newValue) === false
Примеры:
| Операция | Перерисовка? | Причина |
|---|---|---|
| setCount(0) когда count=0 | НЕТ | Object.is(0, 0) === true |
| setCount(1) когда count=0 | ДА | Object.is(0, 1) === false |
| setUser({id:1}) каждый раз | ДА | Новый объект (новая ссылка) |
| setUser(prev => prev) | НЕТ | Та же ссылка |
| setItems([...items, x]) | ДА | Новый массив |
| setIsOpen(false) когда false | НЕТ | Object.is(false, false) === true |
Это оптимизация React, которая экономит ресурсы и улучшает производительность приложения.