it-roy-ru.com

Знание того, когда пользовательский интерфейс готов в React / Redux

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

  1. Загрузка DOM.
  2. Начальный React рендер.
  3. HTTP-вызовы, вызванные различными componentDidMount()s
  4. HTTP-вызовы возвращаются. Состояние Redux обновляется HTTP-ответами.
  5. React рендеринг с новыми значениями из ответов HTTP.
  6. Вся начальная загрузка завершена. Пользователь готов взаимодействовать с приложением.

В конце этого процесса я бы хотел запустить событие отслеживания, чтобы записать значение window.performance.now().

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

Вещи, которые я пробовал или рассматривал:

  1. Ищите ловушку жизненного цикла React, которая сообщает мне, обновляется ли какой-либо дочерний объект. Что-то вроде componentOrAnyChildDidUpdate(). Я не думаю, что это существует, и это может противоречить философии React.
  2. Соберите хук жизненного цикла anyChildDidUpdate() через context и сделайте каждый компонент в моем приложении либо подклассом вспомогательного абстрактного базового класса, либо оберните его в компонент более высокого порядка. Это кажется плохим, потому что context - это экспериментальный API .
  3. Подписаться на состояние Redux через store.subscribe(). Обновите состояние Redux, чтобы получить явную запись о возврате всех HTTP-вызовов. После того, как все HTTP-вызовы вернутся и React завершит повторную визуализацию, я знаю, что нужно запустить обратный вызов отслеживания. Проблема заключается в том, что React завершает повторную визуализацию. Это сработало бы, если бы мой store.subscribe() обратный вызов гарантированно выполнялся синхронно после обратного вызова react-redux. Но это не тот случай.

Есть ли хороший способ сделать это в React?

17
Nick Heiner

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

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

Я решил это так:

1) Я удалил все свои вызовы API изнутри моих компонентов.

2) Создайте компонент Page, чтобы обернуть все компоненты, включенные в эту страницу (вызываемые вашим реагирующим маршрутизатором).

3) Выполнять все запросы, необходимые для ваших компонентов одновременно при навигации (в моем случае при отображении загрузчика). Для каждого запроса, который завершается, создайте обещание, и внутри него создайте свой компонент динамически, используя React.createElement. Передача компоненту созданного ответа на запрос и обработчика обещаний в качестве реквизита.

4) Разрешите обещание в componentDidMount вашего компонента.

5) Как только все обещания будут выполнены, вы знаете, что «страница» готова, и вы можете записать значение window.performance.now().

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

1
Sotiris Kiritsis

Есть несколько способов сделать то, о чем ты просишь, но я бы сделал следующее:

• Создайте перфузионный редуктор и вызовите performance.now() прямо перед тем, как я создаю хранилище редуксов и добавлю значение в исходное состояние.

{ perf: { start: '...', end: '...' } }

• Отслеживать состояние загрузки начальных HTTP-запросов в загруженном редукторе.

{ loaded: { request1: false, request2: false, request3: false } }

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

import React from 'react';
import { connect } from 'react-redux';

class App extends React.Component {
  componentDidUpdate(prevProps) {
    if (!prevProps.loadingComplete && this.props.loadingComplete) {
      this.props.updatePerf(performance.now());
    }
  }
}

const mapStateToProps = state => ({
  loadingComplete: state.loaded.every(item => item),
});

const mapDispatchToProps = dispatch => ({
  updatePerf(time) {
    dispatch({ type: 'SET_ENDING_PERF', payload: time });
  },
});

export default connect(mapStateToProps, mapDispatchToProps)(App);
1
pizza-r0b

В нашем проекте мы используем избыточность и пару действий - триггер, запрос, успех, ошибка. 

«Триггер» вызывает запрос и выбрасывает загрузку: true в наш «умный» компонент «Запрос» извлекает данные и выбрасывает их в «Успех/Ошибка». «Успех», говорит нам, что все в порядке и данные загружаются и выбрасывают { загрузка: ложь, данные}

В случае успеха/ошибки мы всегда знаем, что происходит с нашим приложением

И, конечно, вы можете использовать componentWillReceiveProps (nextProps) {} и проверить флаг загрузки и данные в вашем компоненте.

0
Denis Tronin

Вы можете попробовать использовать react-addons-perf

Вот как я обычно тестирую свои приложения:

По сути, вы хотите начать измерение, когда ваш компонент начинает обновление, и остановить измерение после метода жизненного цикла componentDidUpdate().

import Perf from 'react-addons-perf'

componentWillMount() {
  window.performance.mark('My app');
 }
componentDidMount() {
  console.log(window.performance.now('My app'));
  Perf.start()
  // ... do your HTTP requests
}
componentDidUpdate() {
 Perf.stop();
 Perf.printInclusive();
}

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

Надеюсь, поможет

0
NickHTTPS

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

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

То есть все «ожидаемые» компоненты могли бы предоставлять onLoad - подобную опору для передачи вверх по дереву реакции; корневой компонент будет затем координировать результат, соответственно изменяя дерево вниз по потоку, распространяя соответствующий подпор вниз до листьев.

Это может быть реализовано путем написания компонента mixin/high order для инкапсуляции логики «готовности к обновлению».

0
Massimiliano Janes

Два шага для достижения этого.

Отслеживать все HTTP-запросы через действия

Простого счетчика в состоянии избыточности было бы достаточно, чтобы увеличивать при запуске запроса и уменьшать при успешном или неудачном (все путем соответствующих действий) . Нет window.dirty.stuff здесь перед Дэном Абрамовым.

Отслеживание всех компонентов connected

Метод componentDidUpdate указывает, что все дочерние элементы закончили рендеринг. Это может быть достигнуто без большого количества шаблонов с recompose's lifecycle HOC. Правильным местом для начала отслеживания повторного рендеринга может быть метод sCU или просто cWRP, если вы уже используете recomposes shouldUpdate или reselect, поскольку мы не будем отслеживать компоненты, которые не были обновлены.

Обратите внимание, что все предполагает идиоматическое приложение «Реакция/Избыток», никаких сложных побочных эффектов или анимации. Например, если какой-то дочерний компонент где-то глубоко в иерархии запускает свой собственный запрос AJAX, скажем, на cDM, а не с помощью действия приставки. Таким образом, вы также должны отслеживать их, НО никто не может быть уверен, что они предполагают какие-либо повторные рендерингы .... Отслеживание такого поведения все еще возможно, просто может потребоваться больше усилий.

0
Aman Kubanychbek