Примечание. Это пример неудачной попытки оптимизации, который, как мне показалось, стоит выделить, поскольку он помогает лучше понять жизненный цикл React componentDidUpdate. Если вы уже являетесь экспертом по React или просто хотите перейти к чему-то, что действительно улучшает производительность, см. Следующие шаги внизу!

Разделение компонентов

Поскольку мы пытаемся разделить дорогостоящее обновление результатов поиска из ввода, мы должны создать наши собственные отдельные компоненты вместо использования универсального Autocomplete из react-autocomplete библиотека:

Затем внутри SearchResults нам нужно запускать searchEngine.search(searchTerm) асинхронно, а не на том же рендере, что и первоначальное обновление searchTerm.

Моя первая идея заключалась в том, чтобы использовать componentDidUpdate внутри SearchResults, чтобы searchEngine работал асинхронно, потому что похоже, что это метод, который запускается после обновления.

Мыслительный процесс состоит в том, что, перемещая дорогостоящую searchEngine работу на componentDidUpdate, вы больше не вызываете ее внутри метода render и планируете ее выполнение после обновления. Это будет выполняться в отдельном последующем стеке выполнения из исходного обновления input, позволяя рисовать между ними. Я предположил, что новый жизненный цикл обновления будет выглядеть так:

  • input отображается с новым searchTerm
  • React ставит componentDidUpdate searchEngine задачу в очередь с новым searchTerm в очереди событий JavaScript.
  • Обновление input отображается в браузере перед запуском searchEngine задачи в очереди. Теоретически браузер не будет отдавать приоритет стоящей в очереди задаче над рисованием, потому что задача не является событием взаимодействия с пользователем.
  • componentDidUpdate запускается после раскраски input, вычисляет результаты поиска и инициирует новое обновление с ними

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

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

Хотя componentDidUpdate не ставится в очередь и не запускается в отдельном стеке выполнения, он запускается после того, как React обновляет состояние компонента и фиксирует обновленные значения DOM. Хотя эти обновленные значения DOM еще не отображаются браузером, они по-прежнему отражают то, как будет выглядеть обновленный пользовательский интерфейс. Таким образом, любые поисковые запросы DOM, которые выполняются внутри componentDidUpdate, будут иметь доступ к значениям после обновления. Что касается вашего кода, компонент DID обновился, вы просто еще не видите его в браузере.

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

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

Боковое примечание (перехватчики React). Это различие также полезно для понимания новых ловушек useEffect и useLayoutEffect. useEffect - это то, чего мы здесь пытались достичь. Кажется, что он стоит в очереди и запускает свой код в отдельном стеке выполнения, позволяя рисовать перед запуском. В то время как useLayoutEffect больше похож на componentDidUpdate и позволит вам запускать код после обновлений DOM, но перед отображением этих обновлений на экране.

Как еще мы можем сделать его асинхронным?

v1.1.1 Асинхронный рендеринг (с использованием setTimeout)

v1.2.0 Многопоточность