Оптимизация Рендеринга в React с помощью React.memo и useCallback
Как можно использовать React.memo
и useCallback
для оптимизации рендеринга компонентов в React. Это полезные инструменты для предотвращения ненужных повторных рендеров дочерних компонентов, улучшения производительности и упрощения кода в ситуациях, когда функции передаются в качестве пропсов.
Основная идея примера
В компоненте App есть состояние count
, которое отслеживается с помощью хука useState
. Когда состояние count
обновляется, родительский компонент App рендерится заново. Для управления обновлением состояния создается функция onIncrement
, которая увеличивает значение count
на 1.
Использование React.memo
const ChildComponent = memo(({ onIncrement }) => {
console.log("Рендер дочернего компонента");
return <button onClick={onIncrement}>Увеличить в дочернем компоненте</button>;
});
React.memo
предотвращает повторный рендер ChildComponent
, если его пропсы не изменяются. Однако, если функция onIncrement
пересоздается на каждом рендере родительского компонента, React.memo
будет считать, что пропсы изменились, что приведет к повторному рендеру ChildComponent
.
Проблема пересоздания функции
Каждый раз, когда App
рендерится, создается новая версия функции onIncrement
. Поскольку функция передается как пропс в ChildComponent
, это приводит к его повторному рендеру, даже если count
не изменился.
Решение с useCallback
Хук useCallback
используется для мемоизации функции onIncrement
:
const onIncrement = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []);
Теперь onIncrement
будет пересоздаваться только при изменении зависимостей (в данном случае зависимостей нет, так как массив зависимостей пуст). Это позволяет React.memo
правильно предотвратить повторный рендер ChildComponent
.
Полный код
const ChildComponent = memo(({ onIncrement }: any) => {
console.log("Рендер дочернего компонента");
return <button onClick={onIncrement}>Увеличить в дочернем компоненте</button>;
});
const App: React.FC = () => {
const [count, setCount] = useState(0);
// Без useCallback каждый рендер ParentComponent будет пересоздавать onIncrement,
// вызывая повторный рендер ChildComponent.
const onIncrement = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []);
return (
<div>
<p>count: {count}</p>
<ChildComponent onIncrement={onIncrement} />
</div>
);
};
Благодаря использованию React.memo и useCallback мы добились оптимизации рендеринга. Дочерний компонент ChildComponent рендерится только один раз при первом рендере родительского компонента и больше не рендерится повторно, пока не изменяются его пропсы. Это значительно улучшает производительность, особенно если в вашем приложении есть многоуровневые компоненты или сложные вычисления в дочерних компонентах.