Зная, когда пользовательский интерфейс готов в React/Redux
Я хотел бы знать, как долго мое приложение становится "готовым" для взаимодействия с ним. Временная шкала загрузки приложения включает в себя следующее:
- Загрузка DOM.
- Первоначальный рендер.
- HTTP-вызовы, вызванные различными
componentDidMount()
s
- Возврат HTTP-вызовов. Состояние Redux обновляется с ответами HTTP.
- React отображает новые значения из ответов HTTP.
- Вся начальная загрузка завершена. Пользователь готов к взаимодействию с приложением.
В конце этого процесса я хочу запустить событие отслеживания, чтобы записать значение window.performance.now()
.
Я не уверен, что лучший способ сделать это. Реагирующая система компонентов отлично справляется с развязкой различных частей пользовательского интерфейса. В этом случае это, к сожалению, означает, что у меня не будет одного простого места, чтобы проверить, "все ли сделано с новыми данными".
Вещи, которые я пробовал:
- Посмотрите на крючок жизненного цикла React, который сообщает мне, обновляется ли какой-либо ребенок. Что-то вроде
componentOrAnyChildDidUpdate()
. Я не думаю, что это существует, и это может противоречить философии Реакта.
- Взломайте крючок жизненного цикла
anyChildDidUpdate()
через context
и сделайте каждый компонент в моем приложении либо подклассом вспомогательного абстрактного базового класса, либо обернутым в компонент более высокого порядка. Это кажется плохим, потому что context
является экспериментальным API.
- Подпишитесь в состояние Redux через
store.subscribe()
. Обновите состояние Redux, чтобы иметь явную запись о возврате всех HTTP-вызовов. После того, как все HTTP-вызовы вернулись, и React завершит повторную визуализацию, я знаю, чтобы запустить обратный вызов отслеживания. Проблема заключается в том, что когда React завершает повторный рендеринг. Он работал бы, если бы мой store.subscribe()
обратный вызов гарантированно выполнялся синхронно после обратного вызова react-redux
. Но это не так.
Есть ли хороший способ сделать это в React?
Ответы
Ответ 1
Я боюсь, что в React нет универсально хорошего способа сделать это, это "логика", связанная со структурой вашего приложения.
Я хотел отобразить загрузчик при навигации с одной страницы на другую в приложении с одной страницей (SPA), написанном в ответ, и я столкнулся с аналогичной проблемой, не зная, когда прекратить ее отображение, и если все компоненты на новой странице завершены их API-вызовы/рендеринг.
Я решил, что это было:
1) Я удалил все мои вызовы API изнутри моих компонентов.
2) Создайте компонент страницы, чтобы обернуть все компоненты, включенные в эту страницу (вызывается вашим реагирующим маршрутизатором).
3) Выполняйте все запросы, требуемые для ваших компонентов одновременно при навигации (в моем случае при отображении загрузчика). Для каждого завершаемого запроса создайте обещание и внутри него динамически создайте свой компонент, используя React.createElement. Передайте компонент, созданный ответ запроса, и обработчик обещаний в качестве реквизита.
4) Разрешите обещание в компоненте componentDidMount
.
5) После того, как все promises разрешены, вы знаете, что "страница" готова, и вы можете записать значение window.performance.now()
.
Трудно представить пример минимального кода без большого количества контекстного кода, так как этот код распространяется по всему приложению.
Ответ 2
Есть более чем один способ сделать то, что вы просите, но с моей головы я бы сделал следующее:
• Создайте перфекционный редуктор и вызовите performance.now()
прямо перед тем, как я сделаю хранилище редукции и добавлю значение в начальное состояние.
{ perf: { start: '...', end: '...' } }
• Отслеживать статус загрузки начальных HTTP-запросов в загруженном редукторе.
{ loaded: { request1: false, request2: false, request3: false } }
• Подключите компонент верхнего уровня к загруженному редуктору и проверьте, завершены ли все запросы в компонентеDidUpdate. Если 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);
Ответ 3
В нашем проекте мы используем redux и пару действий - триггер, запрос, успех, ошибку.
"Триггер" вызывает запрос и бросает загрузку: true в наш "умный" компонент
"Запрос" извлекает данные и передает их в "Успех/Ошибка"
"Успех", говорит нам, что все в порядке и данные загружены и бросают {loading: false, data}
В случае успеха/ошибки мы всегда знаем, что происходит с нашим приложением
И, конечно, вы можете использовать componentWillReceiveProps (nextProps) {} и проверять флаг загрузки и данные в своем компоненте
Ответ 4
Вы можете попробовать использовать 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();
}
Подобно этому вы будете отслеживать исходный рендер вашего компонента + обновления вашего компонента.
Надеюсь, что это поможет
Ответ 5
Два шага для этого.
Отслеживать все HTTP-запросы с помощью действий
Простой счетчик в состоянии редукции будет достаточным, что увеличивается при запуске запроса и уменьшается на успех или неудачу (все с помощью соответствующих действий).
Нет window.dirty.stuff
здесь перед Дэн Абрамовым.
Отслеживать все компоненты connect
ed
componentDidUpdate
указывает, что все дети закончили рендеринг. Это может быть достигнуто без большого шаблона с recompose
lifecycle
HOC. Правильное место для повторного рендеринга отслеживания может быть sCU
или просто cWRP
, если вы уже используете recompose
shouldUpdate
или reselect
, поскольку мы не будем отслеживать компоненты, которые не обновлялись.
Обратите внимание, что все принимают идиоматическое приложение action/redux, никаких сложных побочных эффектов или анимаций. Например, если какой-либо дочерний компонент где-то глубоко в иерархии запускает свой собственный запрос AJAX, скажем, на cDM
, а не через действие redux. Таким образом, вы должны отслеживать их тоже, НО никто не может быть уверен, что они подразумевают какие-либо повторные визуализации.
Отслеживание такого поведения по-прежнему возможно, просто потребуется больше усилий.
Ответ 6
из дизайна POV, на мой взгляд, будучи готовым или нет, является свойством представления пользователя и поэтому должно обрабатываться только компонентами реакции.
например, позднее вы можете разрешить пользователю взаимодействовать с какой-то частью вашего пользовательского интерфейса, пока некоторые другие части по-прежнему загружаются, или вы можете решить, что некоторые компоненты должны позволить пользователю взаимодействовать до того, как модель данных будет готова, и т.д., другими словами, он отвечает (реагирует) на то, чтобы сказать, готов ли он/когда он готов или нет, и никто другой.
Таким образом, все "готовые" компоненты могут выставлять onLoad-подобную опору для передачи дерева реакции; корневой компонент затем будет координировать результат, изменив поток дерева вниз соответствующим образом, распространив соответствующие реквизиты до листьев.
Это может быть реализовано путем написания компонента mixin/high order для инкапсуляции логики обновления готовности.