Ответ
Используй React.memo для мемоизации компонентов, если пропсы не изменились. Для сложных вычислений применяй useMemo, а для колбэков — useCallback.
Пример:
const ExpensiveComponent = React.memo(({ data }) => {
const processedData = useMemo(() => heavyComputation(data), [data]);
return <div>{processedData}</div>;
});
const Parent = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => setCount(c => c + 1), []);
return (
<>
<ExpensiveComponent data={count} />
<button onClick={handleClick}>Increment</button>
</>
);
};
Проверяй лишние рендеры через React DevTools (вкладка Profiler). Убедись, что стейт и пропсы действительно меняются перед рендером. Для глобального стейта (Redux, Context) используй селекторы или разбивай контексты, чтобы обновлялись только нужные компоненты.
Ответ 18+ 🔞
Слушай, а вот представь: пишешь ты компонент, он вроде и не тяжёлый, но рендерится как сумасшедший от каждого чиха родителя. Ну, блядь, достало уже! Так вот, чтобы эта мартышлюшка не дёргалась попусту, есть у нас React.memo.
Это как будто ты говоришь Реакту: «Чувак, смотри, у меня пропсы те же самые, нихуя не поменялись. Не перерисовывай меня, а?». Обернул компонент — и всё, он теперь сравнивает пропсы поверхностно. Если они те же — рендер проскакивает.
А теперь, хуй с горы, у тебя внутри компонента есть какая-то ебанутая вычислялка, которая каждый раз пересчитывает овердохуище данных. Ты её каждый рендер вызываешь, а она тебе процессор жрёт. Вот тут в игру вступает useMemo. Засунул туда эту функцию и зависимости — и он тебе результат кеширует. Пока зависимости не ебнулись, будет отдавать старое значение, не пересчитывая. Красота, ёпта!
И третий акт в этом ёперном театре — useCallback. Допустим, ты прокидываешь колбэк в мемоизированный дочерний компонент. А этот колбэк у тебя каждый раз создаётся заново. Для Реакта это новая ссылка, значит, пропс изменился, значит, надо перерендерить. Пиздец! Оберни колбэк в useCallback, и ссылка сохранится, пока зависимости в массиве не поменяются. Дочерний компонент успокоится.
Вот тебе пример, как это всё в паре работает, чтобы ты не ебал мозги:
const ExpensiveComponent = React.memo(({ data }) => {
const processedData = useMemo(() => heavyComputation(data), [data]);
return <div>{processedData}</div>;
});
const Parent = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => setCount(c => c + 1), []);
return (
<>
<ExpensiveComponent data={count} />
<button onClick={handleClick}>Increment</button>
</>
);
};
Видишь? ExpensiveComponent обёрнут в memo. Ему передают data. Внутри него useMemo кеширует результат тяжёлой функции, пока data та же. А колбэк на кнопке завернут в useCallback, так что он не сломает мемоизацию, если Parent вдруг перерендерится по другим причинам.
И главное, блядь, не верь на слово! Открой React DevTools, вкладку Profiler, и посмотри, какие компоненты у тебя там лишний раз дергаются. Убедись, что стейт и пропсы реально меняются, а не просто кажется.
А если у тебя там глобальный стейт, типа Redux или Context, который на весь чертог развешан — тут отдельная песня. Не вешай всё в один контекст, а то любое изменение будет всю приложуху трясти. Разбивай на мелкие контексты или используй селекторы (типа reselect), чтобы вычислялось только то, что реально нужно конкретному компоненту. Иначе получишь пиздопроебибну перформанса. Думай головой!