Автор: Сукджит Сингх Сандху || Редактор: Анита Тогулува

Как мы знаем из предыдущих статей, React работает быстро, поскольку он использует технику виртуального DOM и при каждом рендеринге сравнивает виртуальный DOM с фактическим DOM, и только diff обновляется в фактическом DOM, который отлично работает, когда размер приложения равен маленький. Но по мере того, как приложение растет, появляется множество факторов, вызывающих ненужные повторные отрисовки, и оно начинает замедляться. Не о чем беспокоиться, этих ненужных повторных рендеров и вычислений можно легко избежать. Ниже приведены некоторые из передовых практик, которые являются стандартными и которые мы учитываем при разработке любого компонента.

1. Используйте методы восстановления рендеринга

Каждый раз, когда обновляется родительский компонент, дочерние компоненты обновляются независимо от того, изменились их свойства или нет. Это означает, что даже если дочерний компонент имеет точно такие же свойства, как и раньше, он все равно будет повторно визуализироваться. Это дорого, особенно для больших деревьев компонентов, поскольку, по сути, React должен будет применить алгоритм сравнения, чтобы проверить, есть ли различия между предыдущим и вновь вычисленным деревом.

Этого можно легко избежать, либо расширив компоненты на основе классов из React.PureComponent, используя ловушку жизненного цикла shouldComponentUpdate, либо заключив компоненты в компонент более высокого порядка React.memo.

2. Используйте функциональный компонент вместо класса

В React есть два типа компонентов: компоненты класса и функциональные компоненты. Функциональные компоненты - это просто функции, которые принимают реквизиты в качестве аргумента и возвращают JSX. Функциональные компоненты не имеют состояния и также называются компонентами без состояния. Функциональные компоненты легкие и помогают уменьшить размер пакета. Говорят, что всегда следует начинать с функционального компонента и реорганизовывать его до компонента класса только в случае необходимости. В CodeParva мы анализируем его на этапе проектирования, есть ли необходимость в использовании компонента класса или мы можем управлять с помощью функционального компонента.

3. Избегайте встроенных объектов

Каждый раз, когда вы встраиваете объект, React повторно создает новую ссылку для этого объекта при каждом рендеринге. Это заставляет компоненты, которые получают этот объект, рассматривать его как другой. Таким образом, поверхностное равенство свойств этого компонента будет возвращать false в каждом цикле рендеринга, даже если вы используете React.memo или React.PureComponent.

Неправильный:-

class DemoComponent extends React.PureComponent {
render() {
        return (
              <div dimensions={{ x: 10, y: 10}}>Demo Component</div>
        )
    }
}

Правильно:-

class DemoComponent extends React.PureComponent {
constructor(props) {
        super(props);
        this.dimensions = { x: 10, y: 10 }
    }
render() {
        const { dimensions = {} } = this;
        return (
              <div dimensions={dimensions}>Demo Component</div>
        )
    }
}

4. Избегайте анонимных функций

Хотя анонимные функции - отличный способ передать опору функции (особенно ту, которую нужно вызывать с другой опорой в качестве параметра), они получают разные ссылки при каждом рендеринге. Это похоже на встроенные объекты, описанные выше. Чтобы сохранить ту же ссылку на функцию, которая передается в качестве опоры компоненту React, ее можно либо объявить как метод класса (если это компонент на основе классов) или хук useCallback может использоваться для сохранения той же ссылки (если это функциональный компонент)

Неправильный:-

class DemoComponent extends React.PureComponent {
const onClick = () => {
         // Do Something
    }
render() {
        const { onClick } = this;
        return (
              <button onClick={() => onClick()}>Click Here</button>
        )
}
}

Правильно:-

class DemoComponent extends React.PureComponent {
const onClick = () => {
         // Do Something
    }
render() {
        const { onClick } = this;
        return (
              <button onClick={onClick}>Click Here</button>
        )
}
}

5. Настройте CSS вместо того, чтобы принудительно монтировать и размонтировать компонент

Рендеринг требует больших затрат, особенно когда необходимо изменить DOM. Всякий раз, когда у вас есть какая-то функция аккордеона или вкладки - где вы видите только один элемент за раз - у вас может возникнуть соблазн отключить компонент, который не виден, и установить его обратно, когда он станет видимым.

class DemoComponent extends React.PureComponent {
      
     render() {
         const { state: { showButton = false } } = this;
         return (
             <div>
                  {
                      showButton
                      ? <button>Click Here</button>
                      : <div>Demo Component</div>
                   }
             </div>
         )
     }
}

Если монтируемый / демонтируемый компонент «тяжелый», то эта операция может оказаться более дорогостоящей и вызвать задержку. В таких случаях лучше скрыть его с помощью CSS, сохранив содержимое в DOM.

В качестве бонуса изменение прозрачности до 0 почти не требует затрат для браузера и по возможности должно быть предпочтительнее, чем изменения видимости и отображения.

6. Запомните дорогие расчеты

Бывают случаи, когда рендеринг неизбежен, но поскольку ваш компонент React является функциональным, ваш рендеринг вызывает пересчет любых вычислений, которые у вас есть внутри компонента. Вычисления, значения которых не меняются при каждом рендеринге, можно «запоминать» с помощью нового хука useMemo. Благодаря этому вы можете избавиться от дорогостоящих вычислений, используя значение, вычисленное на основе предыдущего рендеринга.

Например, компонент вычисляет площадь, используя параметры «длина» и «ширина». В этом случае нам нужно пересчитывать площадь только при изменении «длины» или «ширины» опоры. Этого можно добиться с помощью хука useMemo, и он не будет вычислять площадь при каждом рендеринге.

const DemoComponent = (props) => {
const { length, width } = props;
    const area = React.useMemo(() => (length * width)
                               , [length, width])
return (<div>Demo Component</div>)
}

7. Способы анализа причин повторных рендеров

  • Используйте сторонние библиотеки, например, why-did-you-render.
  • Профилирование компонентов с помощью вкладки "Производительность Chrome".

использованная литература

  1. Функциональные и классовые компоненты: https://reactjs.org/docs/components-and-props.html#function-and-class-components.
  2. Почему вы сделали рендеринг: https://www.npmjs.com/package/@welldone-software/why-did-you-render.
  3. Вкладка Производительность Chrome: https://reactjs.org/docs/optimizing-performance.html#profiling-components-with-the-chrome-performance-tab.

Чтобы узнать больше об этом, вы всегда можете связаться с нами. Посетите CodeParva Technologies, чтобы узнать о нас больше. Linkedin | Инстаграм