<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Почему useCallback не спасает от ре-рендеров в memo-компонентах]]></title><description><![CDATA[<p dir="auto">Junior’ы часто думают, что <strong>useCallback</strong> - это волшебная палочка против ре-рендеров. Обернул функцию, и memo-компонент больше не дергается. На деле это всего лишь маскировка: функция стабилизируется, но ребенок все равно рендерится из-за inline-объектов или кривых зависимостей.</p>
<p dir="auto">Разберем, почему так происходит. Ты увидишь реальные примеры, где useCallback бесполезен, и поймешь, где копать глубже. Это сэкономит часы дебага и избавит от иллюзий про ‘оптимизированный’ код.</p>
<h2>Как работает useCallback под капотом</h2>
<p dir="auto">useCallback кэширует функцию, сравнивая зависимости по <strong>shallow equal</strong>. Если массив deps не изменился - возвращает старую функцию. Звучит круто, но React.memo тоже использует shallow compare для пропсов. Функция стабилизируется, но если рядом inline-объект или массив - пропсы не совпадут, и рендер полетит.</p>
<p dir="auto">Представь: родительский компонент рендерится из-за setState. Он создает новый объект { onClick, data: [1,2,3] } каждый раз inline. memo-дитя видит новые пропсы - shallow compare проваливается. useCallback на onClick тут как пластырь на перелом: функция старая, но объект новый - ре-рендер неизбежен.</p>
<p dir="auto">Ключевые проблемы с зависимостями:</p>
<ul>
<li><em>Зависимости мутируют</em> - React не видит изменений в объектах, если ссылка та же.</li>
<li>Inline-объекты создаются заново при каждом рендере.</li>
<li>Контекст или пропсы от родителя ломают всю схему.</li>
</ul>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Ситуация</th>
<th>useCallback помогает?</th>
<th>Почему рендерится</th>
</tr>
</thead>
<tbody>
<tr>
<td>Inline функция</td>
<td>Да</td>
<td>-</td>
</tr>
<tr>
<td>Inline объект в пропсах</td>
<td>Нет</td>
<td>Shallow compare пропсов</td>
</tr>
<tr>
<td>Мутация deps</td>
<td>Нет</td>
<td>Ссылка не меняется</td>
</tr>
<tr>
<td>React.memo без deps</td>
<td>Нет</td>
<td>Родитель рендерит детей по умолчанию</td>
</tr>
</tbody>
</table>
<h2>Почему memo-компоненты рендерятся несмотря на useCallback</h2>
<p dir="auto">memo оборачивает компонент, пропуская рендер, если пропсы shallow equal. useCallback стабилизирует только функцию, но не трогает объекты или массивы в пропсах. Родитель дернулся - передал { handler: useCallback(fn), items:  } - новый массив каждый раз, memo срабатывает на рендер.</p>
<p dir="auto">Пример: список пользователей. Родитель имеет state users = [{id:1, name:‘Bob’}]. Передаем в ChildList: props = { users, onDelete: useCallback(deleteUser, [users]) }. users меняется - deps меняются - новая функция. Даже если deps пустые, inline slice() или map создаст новый массив.</p>
<p dir="auto">Типичные грабли:</p>
<ul>
<li><strong>Забытые deps</strong> - eslint-plugin-react-hooks ругается зря, функция захватывает замыкания.</li>
<li><em>Inline объекты</em> - { id, label: name } вместо стабильного объекта из useMemo.</li>
<li>Массивы из map без memo - каждый рендер новый массив ссылок.</li>
</ul>
<pre><code>// Плохо
const Child = ({ items, onClick }) =&gt; &lt;ul&gt;{items.map(item =&gt; &lt;li key={item.id} onClick={() =&gt; onClick(item)} /&gt;)}&lt;/ul&gt;;
// items новый массив -&gt; ре-рендер
</code></pre>
<h2>Утечки через неоптимальные зависимости</h2>
<p dir="auto">Зависимости в useCallback - это shallow compare. Объект dep1 = {a:1} не изменится по ссылке, даже если внутри a поменялось. React подумает, что deps stable - вернет старую функцию. Но в замыкании fn захватит старые данные - баги в runtime.</p>
<p dir="auto">Реальный кейс: фильтр списка. deps = [filterObj], filterObj мутируется setFilter({ …filterObj, value: newVal }). Ссылка та же - useCallback вернет старую fn с устаревшим замыканием. Дитя получит stable fn, но данные черствые.</p>
<p dir="auto">Как фиксить:</p>
<ol>
<li><em>Хуки для объектов</em> - useMemo для стабильных пропсов.</li>
<li>Дроби deps на примитивы - id вместо объекта.</li>
<li>Custom comparer для memo - React.memo с areEqual функцией.</li>
</ol>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>deps</th>
<th>Shallow equal?</th>
<th>Проблема</th>
</tr>
</thead>
<tbody>
<tr>
<td>Примитивы</td>
<td>Да</td>
<td>-</td>
</tr>
<tr>
<td>Новый объект</td>
<td>Нет</td>
<td>Ре-рендер</td>
</tr>
<tr>
<td>Мутация объекта</td>
<td>Да</td>
<td>Устаревшее замыкание</td>
</tr>
<tr>
<td>useMemo объект</td>
<td>Да</td>
<td>Стабильно</td>
</tr>
</tbody>
</table>
<h2>Маскировка вместо рефакторинга</h2>
<p dir="auto">useCallback часто юзают как костыль: ‘рендерится - добавим callback’. Вместо фикса архитектуры - state lift или Context - лепят мемо. Производительность маскируется, но бандл растет, дебажить сложнее.</p>
<p dir="auto">Под капотом: ивент-луп не блокируется, React батчит рендеры, но лишние diff’ы жрут CPU. Junior радуется React DevTools Profiler - ‘зеленые блоки!’ - не зная, что под водой утечки.</p>
<p dir="auto">Факты начистоту:</p>
<ul>
<li>useCallback + memo спасает только shallow пропсы.</li>
<li><em>Более 70% ре-рендеров</em> от inline объектов (по бенчмаркам).</li>
<li>Лучше рефакторить: выноси state в reducer, используй compound components.</li>
</ul>
<h2>Рефакторинг вместо иллюзий оптимизации</h2>
<p dir="auto"><strong>useCallback</strong> - не панацея, а инструмент в арсенале. Маскирует симптомы, но не лечит: inline-объекты и кривые deps остаются. Подумай о структуре: зачем Child зависит от всего массива? Раздели на маппинг с ключами, stable handlers через id.</p>
<p dir="auto">Осталось за кадром: custom hooks для батчинга пропсов или Reselect-подобные селекторы. Если копнешь в scheduler - увидишь, почему даже memo не всегда спасает от parent re-renders. В следующий раз разберем, как ивент-луп рвет твои оптимизации.</p>
]]></description><link>https://forum.exlends.com/topic/2002/pochemu-usecallback-ne-spasaet-ot-re-renderov-v-memo-komponentah</link><generator>RSS for Node</generator><lastBuildDate>Mon, 06 Apr 2026 22:02:15 GMT</lastBuildDate><atom:link href="https://forum.exlends.com/topic/2002.rss" rel="self" type="application/rss+xml"/><pubDate>Mon, 06 Apr 2026 08:56:44 GMT</pubDate><ttl>60</ttl></channel></rss>