[Перевод] Незнание основ React, которое, возможно, вас губит

Хотите получить наглядное представление о том, что происходит с компонентами, когда вы работаете с React? Читайте под катом перевод статьи Ohans Emmanuel, опубликованной на сайте freeCodeCamp.

tcdo3ukjorqeior-t3qnyhzkkhc.png

Часто устранить определенную ошибку не получается потому, что не знаешь каких-то основ. По этой же причине бывает трудно освоить более продвинутые техники.

В этой статье я попробую рассказать о некоторых принципах работы React, которые, по моему мнению, вам необходимо понимать.

Мы не будем разбирать эти принципы с технической точки зрения. Есть масса других статей, в которых рассматриваются такие понятия, как свойства компонента (props), состояние (state), контекст (context), изменение состояния компонента (setState) и прочие.

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

Готовы?

Скрытые процессы React


Первое, что каждый узнает в React, — это то, как создавать компоненты. Я уверен, что вы тоже этому учились.

Например:

// functional component 
function MyComponent() {
  return 
My Functional Component
} // class based component class MyComponent extends React.Component { render() { return
My Class Component
} }


Большая часть компонентов, которые вы прописываете, возвращает вам некоторые элементы.

function MyComponent() {
  return  My Functional Component  //span element
}

class MyComponent extends React.Component {
  render() {
     return 
My Class Component
//div element } }


Изнутри этот процесс выглядит так: большинство компонентов возвращают дерево элементов.

y_z2ypz2oazgec7u4kgxjyyaras.jpegПосле внутренней оценки компоненты часто возвращают дерево элементов

Кроме того, вы наверняка помните, что компоненты работают как функции, возвращающие значения на основании своих значений props и state.

kcoxcf4j_4byaslxyotu32zkn6i.jpegКомпоненты — это что-то вроде функций с параметрами props и state

Следовательно, всякий раз, когда значения свойств (props) и состояния (state) компонента меняются, создается новое дерево элементов.

cyh33yjvy_hunyzdyz3tizzr8q8.jpegЕсли значения props или state меняются, дерево элементов перерисовывается. В результате появляется новое дерево элементов

Если компонент основан на наследовании классов, дерево элементов возвращает функция

render.

class MyComponent extends React.Component {
  
  render() {
    //this function is invoked to return the tree of elements
  }
}


Если же компонент функциональный, его возвращаемое значение дает дерево элементов.

function MyComponent() {
  
   // the return value yields the tree of elements
   return 
}


Почему это важно?

Рассмотрим компонент , который принимает prop, как показано ниже.



Рендеринг этого компонента возвращает дерево элементов.

_y3auopjwpzvkqdk6ybsgnw-wt0.jpeg
Дерево элементов, возвращаемое после перерисовки

Что происходит, когда значение name меняется?



Ну, возвращается новое дерево элементов!

4ngzjinuntgqibvza9exckasmds.jpeg
НОВОЕ дерево элементов, возвращаемое после перерисовки с другими props

Хорошо.
Теперь в распоряжении React два разных дерева — предыдущее и текущее дерево элементов.
На этом этапе React сравнивает оба дерева, чтобы найти изменения.

54nsl4ekzvcpklennalo-xirwdg.jpeg
Два разных дерева. Что же именно изменилось?

Дерево не изменилось полностью, а лишь частично обновилось (так происходит в большинстве случаев).

После сравнения React обновляет фактический DOM с учетом изменений в новом дереве элементов.

Все просто, не так ли?

Сравнение двух деревьев на предмет изменений называется «согласование». Мы с вами смогли разобрать этот процесс, несмотря на то что он достаточно сложный.

React обновляет только необходимое, правда?


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

cinposam2j3r01k9xooqmgwbx1q.gif
Из React Docs: инспектор DOM, показывающий детали обновления

Все ли так?

Все так.

Однако помните: прежде чем перейти к обновлению DOM, React построит дерево элементов для различных компонентов и проделает необходимое сравнение. Проще говоря, он найдет различия между предыдущим деревом элементов и текущим.

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

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

Ненужный рендеринг vs визуальные обновления


Даже если дерево элементов компонента маленькое, его рендеринг занимает некоторое время (хотя бы незначительное). Чем больше дерево элементов компонента, тем больше времени занимает рендеринг.

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

Позвольте мне показать это на простом примере.

Представьте себе приложение со структурой компонентов, как на иллюстрации ниже.

rgu9uoiv_6f79jjdahkjkhuydsw.jpeg
Приложение с родительским компонентом A и дочерними компонентами B, C и D

Общий компонент-контейнер A получает определенное свойство. Однако делается это только для того, чтобы передать это свойство компоненту D.

6fe1o_xjvwjbiiu906fqn8wjefg.jpeg
Родительский компонент A получает некоторые свойства и передает их дальше дочернему компоненту D

Теперь, когда изменяется значение свойства в компоненте A, все дочерние элементы A перерисовываются для вычисления нового дерева элементов.

wmsubma56nla87wztcj8x025jbe.jpeg
5xoubasi0fpza771pgeybfatd74.jpeg
Когда родительский компонент получает новые свойства, каждый дочерний элемент перерисовывается, и возвращается новое дерево

Соответственно, компоненты B и С также повторно рендерятся, даже если они не изменились вообще! Они не получили никаких новых свойств!

Эта лишняя перерисовка и есть ненужный рендеринг.

В этом примере компоненты B и C перерисовывать не нужно, но React об этом не знает.

Есть много способов решить эту проблему, и я описывал их в моей недавней статье How to Eliminate React Performance Issues («Как минимизировать проблемы производительности React»).

Идем дальше. Посмотрите на приложение ниже.

xvreuz22xxzpadhgtcb2kdet2he.gif
Cardie в действии :)

Я назвал это приложение Cardeу.

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

a3yv4qeubwcgpprlrbxxk1nvdou.gif
Активируйте визуальное отображение обновлений (Paint Flashing) с помощью Chrome DevTools

Теперь мне видно, что было обновлено в DOM.

Это визуальный способ отмечать элементы, которые нужно обновить в DOM. Обратите внимание на зеленую подсветку вокруг текста I am a Librarian («Я библиотекарь».)

Это все здорово, конечно, но меня беспокоит исходный рендеринг дерева элементов компонентов React.

Я могу проверить и его.

ukjneicu8bwkhxnjti9zt_ypxq4.gif
Поставьте галочку в React DevTools, чтобы включить подсветку обновляемых элементов

Теперь я вижу, какие компоненты перерисовываются на самом деле, когда я нажимаю эту кнопку.

ddj0j1mwaqv5-2rdqgrgjjd_ule.gif
Обратите внимание на зеленую подсветку вокруг карты пользователя

Вы видите, насколько отличаются визуальный способ отмечать элементы, которые нужно обновить в DOM, и обновления рендеринга, которые проводит сам React?

React перерисовывает всю карту пользователя, а обновляется только короткий текст.

И это важно.

Заключение


Думаю, что теперь у вас появилось более наглядное представление о том, что и как происходит с вашими компонентами в React.

На самом деле, происходит гораздо больше, чем я вам сейчас тут рассказал. Тем не менее, это хорошее начало.

Вперед — к созданию крутых приложений!

Учитесь работать с React/Redux? Если да, у меня есть отличная серия книг, посвященная Redux. Некоторые говорят, что это лучшая техническая литература, которую они читали!

© Habrahabr.ru