Оптимизация Рендеринга в 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 рендерится только один раз при первом рендере родительского компонента и больше не рендерится повторно, пока не изменяются его пропсы. Это значительно улучшает производительность, особенно если в вашем приложении есть многоуровневые компоненты или сложные вычисления в дочерних компонентах.