Объясни, будет ли перерисовка компонента, если в setState передать тот же state: от чего это зависит и почему.
Рендеринг и производительность
Зависит от того, что значит «тот же»: React сравнивает старое и новое значение через Object.is. Тот же примитив или та же ссылка — перерендера не будет; новый объект с тем же содержимым — будет.
Функциональные компоненты (useState)
const [count, setCount] = useState(5);
setCount(5); // Object.is(5, 5) → рендер пропущен
const [user, setUser] = useState({ name: 'Аня' });
setUser(user); // та же ссылка → рендер пропущен
setUser({ ...user }); // НОВАЯ ссылка → рендер будет, хоть содержимое то жеПравило: Object.is(старое, новое) → обновление игнорируется (bail out). Поэтому мутировать и передавать тот же объект бессмысленно — React не увидит изменений, а копировать без изменений — лишний рендер.
Нюанс: рендер «на всякий случай»
Даже при равном значении React иногда вызывает рендер этого компонента ещё один раз (прежде чем «сдаться»), но не рендерит детей и не коммитит в DOM — на практике это не важно, но в console.log можно увидеть.
Классовые компоненты (setState)
this.setState({ count: 5 }) вызывает перерендер всегда, даже с тем же значением: setState мерджит объект и не сравнивает содержимое. Отсюда shouldComponentUpdate/PureComponent — ручная отмена лишних рендеров.
Практические следствия
- Иммутабельность обязательна: изменение через мутацию + setState(тот же объект) = потерянное обновление.
- Возврат того же значения из функционального сеттера (
setX((x) => x)) — легальный способ «не обновлять». - В редьюсере useReducer вернуть прежний state → рендер пропущен.
Коротко для собеседования
useState сравнивает через Object.is: тот же примитив/ссылка — рендера нет, новый объект с тем же содержимым — рендер есть. В классах setState рендерит всегда (без сравнения) — там спасают PureComponent/shouldComponentUpdate. Отсюда два правила: не мутировать и не создавать новые ссылки без надобности.