Українська

Посібник з оптимізації продуктивності React за допомогою useMemo, useCallback та React.memo. Запобігайте зайвим перерендерам і покращуйте досвід користувача.

Оптимізація продуктивності React: опановуємо useMemo, useCallback та React.memo

React, популярна JavaScript-бібліотека для створення інтерфейсів користувача, відома своєю компонентною архітектурою та декларативним стилем. Однак, у міру зростання складності застосунків, продуктивність може стати проблемою. Непотрібні перерендери компонентів можуть призвести до уповільнення роботи та поганого досвіду користувача. На щастя, React надає кілька інструментів для оптимізації продуктивності, зокрема useMemo, useCallback та React.memo. Цей посібник детально розглядає ці техніки, надаючи практичні приклади та корисні поради, які допоможуть вам створювати високопродуктивні React-застосунки.

Розуміння перерендерів у React

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

Чому компоненти перерендеряться

Мета оптимізації продуктивності — запобігти непотрібним перерендерам, гарантуючи, що компоненти оновлюються лише тоді, коли їхні дані дійсно змінилися. Розглянемо сценарій, що включає візуалізацію даних у реальному часі для аналізу фондового ринку. Якщо компоненти діаграм перерендерюються без потреби при кожному незначному оновленні даних, застосунок стане невідгукливим. Оптимізація перерендерів забезпечить плавний та чутливий досвід користувача.

Представляємо useMemo: мемоізація дорогих обчислень

useMemo — це хук React, який мемоізує результат обчислення. Мемоізація — це техніка оптимізації, яка зберігає результати дорогих викликів функцій і повторно використовує ці результати, коли ті ж самі вхідні дані з'являються знову. Це дозволяє уникнути непотрібного повторного виконання функції.

Коли використовувати useMemo

Як працює useMemo

useMemo приймає два аргументи:

  1. Функція, яка виконує обчислення.
  2. Масив залежностей.

Функція виконується лише тоді, коли змінюється одна із залежностей у масиві. В іншому випадку useMemo повертає раніше мемоізоване значення.

Приклад: обчислення послідовності Фібоначчі

Послідовність Фібоначчі — це класичний приклад обчислювально інтенсивної операції. Створімо компонент, який обчислює n-те число Фібоначчі за допомогою useMemo.


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

function Fibonacci({ n }) {
  const fibonacciNumber = useMemo(() => {
    console.log('Обчислення Фібоначчі...'); // Демонструє, коли запускається обчислення
    function calculateFibonacci(num) {
      if (num <= 1) {
        return num;
      }
      return calculateFibonacci(num - 1) + calculateFibonacci(num - 2);
    }
    return calculateFibonacci(n);
  }, [n]);

  return 

Fibonacci({n}) = {fibonacciNumber}

; } function App() { const [number, setNumber] = useState(5); return (
setNumber(parseInt(e.target.value))} />
); } export default App;

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

Представляємо useCallback: мемоізація функцій

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

Коли використовувати useCallback

Як працює useCallback

useCallback приймає два аргументи:

  1. Функція, яку потрібно мемоізувати.
  2. Масив залежностей.

Функція створюється заново лише тоді, коли змінюється одна із залежностей у масиві. В іншому випадку useCallback повертає той самий екземпляр функції.

Приклад: обробка кліку на кнопку

Створімо компонент із кнопкою, яка викликає функцію-колбек. Ми використаємо useCallback для мемоізації цієї функції.


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

function Button({ onClick, children }) {
  console.log('Кнопка перерендерюється'); // Демонструє, коли кнопка перерендерюється
  return ;
}

const MemoizedButton = React.memo(Button);

function App() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log('Кнопку натиснуто');
    setCount((prevCount) => prevCount + 1);
  }, []); // Порожній масив залежностей означає, що функція створюється лише один раз

  return (
    

Count: {count}

Increment
); } export default App;

У цьому прикладі функція handleClick створюється лише один раз, оскільки масив залежностей порожній. Коли компонент App перерендерюється через зміну стану count, функція handleClick залишається тією ж самою. Компонент MemoizedButton, обгорнутий у React.memo, перерендерюватиметься лише у випадку зміни його пропсів. Оскільки пропс onClick (handleClick) залишається незмінним, компонент Button не перерендерюється без потреби. Уявіть собі інтерактивну карту. Кожного разу, коли користувач взаємодіє з нею, це може впливати на десятки компонентів кнопок. Без useCallback ці кнопки непотрібно перерендерюватимуться, що створює затримки. Використання useCallback забезпечує більш плавну взаємодію.

Представляємо React.memo: мемоізація компонентів

React.memo — це компонент вищого порядку (HOC), який мемоізує функціональний компонент. Він запобігає перерендеру компонента, якщо його пропси не змінилися. Це схоже на PureComponent для класових компонентів.

Коли використовувати React.memo

Як працює React.memo

React.memo обгортає функціональний компонент і виконує поверхневе порівняння попередніх і наступних пропсів. Якщо пропси однакові, компонент не буде перерендерюватися.

Приклад: відображення профілю користувача

Створімо компонент, який відображає профіль користувача. Ми використаємо React.memo, щоб запобігти непотрібним перерендерам, якщо дані користувача не змінилися.


import React from 'react';

function UserProfile({ user }) {
  console.log('UserProfile перерендерюється'); // Демонструє, коли компонент перерендерюється
  return (
    

Name: {user.name}

Email: {user.email}

); } const MemoizedUserProfile = React.memo(UserProfile, (prevProps, nextProps) => { // Кастомна функція порівняння (необов'язково) return prevProps.user.id === nextProps.user.id; // Перерендерювати, лише якщо змінюється ID користувача }); function App() { const [user, setUser] = React.useState({ id: 1, name: 'John Doe', email: 'john.doe@example.com', }); const updateUser = () => { setUser({ ...user, name: 'Jane Doe' }); // Зміна імені }; return (
); } export default App;

У цьому прикладі компонент MemoizedUserProfile буде перерендерюватися лише тоді, коли змінюється пропс user.id. Навіть якщо інші властивості об'єкта user змінюються (наприклад, ім'я або електронна пошта), компонент не буде перерендерюватися, доки ID не стане іншим. Ця кастомна функція порівняння в `React.memo` дозволяє точно контролювати, коли компонент перерендерюється. Уявіть собі платформу соціальної мережі з профілями користувачів, що постійно оновлюються. Без `React.memo` зміна статусу або фотографії профілю користувача викликала б повний перерендер компонента профілю, навіть якщо основні дані користувача залишаються незмінними. `React.memo` дозволяє робити цільові оновлення та значно покращує продуктивність.

Поєднання useMemo, useCallback та React.memo

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

Приклад: складний компонент

Створімо більш складний компонент, який демонструє, як поєднувати ці техніки.


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

function ListItem({ item, onUpdate, onDelete }) {
  console.log(`ListItem ${item.id} перерендерюється`); // Демонструє, коли компонент перерендерюється
  return (
    
  • {item.text}
  • ); } const MemoizedListItem = React.memo(ListItem); function List({ items, onUpdate, onDelete }) { console.log('List перерендерюється'); // Демонструє, коли компонент перерендерюється return (
      {items.map((item) => ( ))}
    ); } const MemoizedList = React.memo(List); function App() { const [items, setItems] = useState([ { id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }, { id: 3, text: 'Item 3' }, ]); const handleUpdate = useCallback((id) => { setItems((prevItems) => prevItems.map((item) => item.id === id ? { ...item, text: `Updated ${item.text}` } : item ) ); }, []); const handleDelete = useCallback((id) => { setItems((prevItems) => prevItems.filter((item) => item.id !== id)); }, []); const memoizedItems = useMemo(() => items, [items]); return (
    ); } export default App;

    У цьому прикладі:

    Таке поєднання технік гарантує, що компоненти перерендерюються лише за необхідності, що призводить до значного підвищення продуктивності. Уявіть собі масштабний інструмент для управління проєктами, де списки завдань постійно оновлюються, видаляються та перевпорядковуються. Без цих оптимізацій будь-яка невелика зміна у списку завдань викликала б каскад перерендерів, роблячи застосунок повільним і невідгукливим. Стратегічне використання useMemo, useCallback та React.memo дозволяє застосунку залишатися продуктивним навіть при роботі зі складними даними та частими оновленнями.

    Додаткові техніки оптимізації

    Хоча useMemo, useCallback та React.memo є потужними інструментами, вони не єдині способи оптимізації продуктивності React. Ось ще кілька технік, які варто розглянути:

    Глобальні аспекти оптимізації

    При оптимізації React-застосунків для глобальної аудиторії важливо враховувати такі фактори, як затримка мережі, можливості пристроїв та локалізація. Ось кілька порад:

    Висновок

    Оптимізація продуктивності React-застосунків є ключовою для забезпечення плавного та чутливого досвіду користувача. Опанувавши такі техніки, як useMemo, useCallback та React.memo, а також враховуючи глобальні стратегії оптимізації, ви зможете створювати високопродуктивні React-застосунки, які масштабуються для задоволення потреб різноманітної бази користувачів. Не забувайте профілювати ваш застосунок, щоб виявити вузькі місця у продуктивності та застосовувати ці техніки оптимізації стратегічно. Не оптимізуйте передчасно – зосередьтеся на тих областях, де ви можете досягти найзначнішого ефекту.

    Цей посібник надає міцну основу для розуміння та впровадження оптимізацій продуктивності в React. As you continue to develop React applications, remember to prioritize performance and continuously seek out new ways to improve the user experience.