Як зменшити рендеринг додатка в React: Глибоке занурення в перерендери

Текст перекладу

React - це потужна бібліотека для створення інтерфейсів користувача, але однією з найпоширеніших проблем із продуктивністю, з якими стикаються розробники, є надмірні перерендери. Перерендери можуть уповільнити вашу програму, особливо коли йдеться про великі та складні компоненти. У цій статті ми розглянемо, чому виникають перерендери, як виявити непотрібні перерендери та як оптимізувати ваш React-додаток для зменшення рендеринга.

Чому виникають перерендери в React

У React перерендери відбуваються, коли змінюється стан компонента. Алгоритм узгодження React потім оновлює DOM, щоб відобразити ці зміни. Хоча перерендери необхідні для підтримки синхронізації інтерфейсу з даними, непотрібні перерендери можуть призвести до проблем з продуктивністю.

Поширені причини непотрібних перерендерів:

  1. Зміни стану: Коли змінюється стан компонента, це викликає перерендер цього компонента та всіх його нащадків.

2. Зміни в Context: Коли змінюється значення в React Context, всі компоненти, які використовують цей Context, будуть перерендерені, навіть якщо вони не використовують ту частину Context, яка змінилася.

3. Перерендери батьківських компонентів: Якщо батьківський компонент перерендерюється, всі його нащадки також будуть перерендерені за замовчуванням, навіть якщо їхні пропси не змінилися.

pic

Як зменшити перерендери в React

Давайте розглянемо деякі техніки для мінімізації непотрібних перерендерів у вашому React-додатку.

1. Меморизація за допомогою React.memo

import React from 'react';  

const MyComponent = React.memo(({ name }) => {  
 console.log('Rendering MyComponent');  
 return 
{name}
;   });      export default MyComponent; ```  
В цьому прикладі компонент `MyComponent` буде перерендерюватися лише в разі зміни пропса `name`. Якщо батьківський компонент буде перерендерюватися, але пропс `name` залишиться незмінним, компонент `MyComponent` не буде перерендерюватися.  

### 2. Використання `useMemo` і `useCallback`

`useMemo` та `useCallback` - це хуки, які допомагають вам меморизувати значення та функції відповідно. Це особливо корисно, коли передаєте зворотні виклики (callback) або складні об'єкти як пропси.

import React, { useMemo, useCallback } from 'react';

const ParentComponent = () => {
const [count, setCount] = React.useState(0);

const expensiveCalculation = useMemo(() => {
console.log('Calculating...');
return count * 2;
}, [count]);

const handleClick = useCallback(() => {
console.log('Button clicked');
}, []);

return (
Count: {count}
Calculation: {expensiveCalculation}
setCount(count + 1)}>Increment
);
};

const ChildComponent = React.memo(({ onClick }) => {
console.log('Rendering ChildComponent');
return Click Me;
});

export default ParentComponent; ```

У цьому прикладі:
- expensiveCalculation меморизується за допомогою useMemo, тому він перераховується лише тоді, коли змінюється count.
- handleClick меморизується за допомогою useCallback, тому він не змінюється між перерендери, що запобігає непотрібним перерендерам компонента ChildComponent.

3. Розділення постачальників Context

Коли ви використовуєте React Context, всі компоненти, які споживають цей Context, будуть перерендерюватися кожного разу, коли змінюється значення Context.
текст перекладу

Щоб мінімізувати це, ви можете розділити ваш Context на менші, більш сфокусовані постачальники.

import React, { createContext, useContext, useMemo } from 'react';  

const DataContext = createContext();  
const ApiContext = createContext();  

const App = () => {  
 const [data, setData] = React.useState({});  
 const api = useMemo(() => ({  
 fetchData: () => {  
 // Логіка отримання даних  
 },  
 }), []);  

 return (  





 );  
};  

const ChildComponent = () => {  
 const data = useContext(DataContext);  
 const api = useContext(ApiContext);  

 return (  

Data: {JSON.stringify(data)}
Fetch Data    
    );   };      export default App; ```  
В цьому прикладі:  
- `DataContext` містить дані, які часто змінюються.  
- `ApiContext` містить API-функції, які рідко змінюються.  
- Розділивши Context, компоненти, які використовують лише `ApiContext`, не будуть перерендерюватися, коли змінюється `DataContext`.  

## 4. Переміщення стану вниз

Якщо стан потрібен лише маленькій частині вашого дерева компонентів, перемістіть його до найнижчого можливого компонента. Це запобіжить непотрібним перерендерям всього дерева.

import React, { useState } from 'react';

const ParentComponent = () => {
return (
);
};

const Header = () => {
return
Header
;
};

const Content = () => {
const [count, setCount] = useState(0);
return (
Count: {count}
setCount(count + 1)}>Increment
);
};

export default ParentComponent; ```

В цьому прикладі стан count переміщено до компонента Content, тому лише Content перерендерюється, коли стан змінюється, а не весь ParentComponent.

5. Використання key для скидання стану

Іноді ви хочете примусово скинути стан компонента, коли змінюються певні умови. Для цього можна використовувати пропс key.

import React, { useState } from 'react';      

const App = () => {    
  const [userId, setUserId] = useState(1);       
  return (    
    setUserId(userId + 1)}>Change User        
  );   
};      

const UserProfile = ({ userId }) => {    
  const [profile, setProfile] = useState({});       
  React.useEffect(() => {    
    // Отримання профілю користувача    
    console.log('Fetching profile for user:', userId);    
  }, [userId]);       
  return 
User Profile: {userId}
;   
};      

export default App; ```  

У цьому прикладі зміна `userId` скидає компонент `UserProfile`, що забезпечує отримання нових даних для нового користувача.  

> оскільки батьківський компонент не перерендерюється, дочірній компонент також не буде перерендерюватися. Це насправді золоте правило, коли мова йде про пропси. Щоб дочірній компонент перерендерився через зміни пропсів, батьківський компонент повинен перерендеритися та передати оновлені пропси. Оскільки в цьому випадку батьківський компонент сам не перерендерюється, дочірній компонент також не перерендерюється.  

## Висновок

Зменшення непотрібних перерендерів у React є важливим для підтримки продуктивного додатку. Використовуючи техніки, як меморизація (`React.memo`, `useMemo`, `useCallback`), розділення постачальників Context, переміщення стану вниз та використання пропса `key`, ви можете значно покращити продуктивність вашого додатку.  

Для подальшого читання, ознайомтесь з цими відео:  
- [CODEstantine Developers Community: React Re-renders](https://www.youtube.com/watch?v=NcDue9cmPh0)  
- [DeveloperWay: React Performance Optimization](https://www.youtube.com/watch?v=qTDnwmMF5q8)



Перекладено з: [How to Reduce App Rendering in React: A Deep Dive into Re-renders](https://medium.com/@mohammedmelshahawy/how-to-reduce-app-rendering-in-react-a-deep-dive-into-re-renders-47aa1db8b153)

Leave a Reply

Your email address will not be published. Required fields are marked *