После пересмотра всей архитектуры я понял, что мне нужно вручную установить начальное состояние компонентов. Теперь начальный рендеринг выполняет полезную работу, а второй рендеринг будет игнорироваться при обнаружении изменения реакции.
У меня все еще есть дополнительные циклы рендеринга. Тем не менее, я вижу, что это положение дел с обнаружением изменений. Многие вещи запускают второй рендеринг: инициализация, маршрутизатор, обработчики событий, наблюдаемые. Пока React использует виртуальный домен для обнаружения изменений, чтобы отсеять значения, которые на самом деле не меняются, реального влияния на производительность не должно быть. Как говорится: я лаю не на том дереве.
state.service.tsx
/** Access state changes as an observable stream */
export const store$ = new Observable<AppState>(observer => {
// All state store observable use `distinctUntilChanged()` operator.
// Without this initial state, `distinctUntilChanged()` will be unable to compare previous and current state.
// As a result, the webapi observable will miss the first response fron the server.
observer.next(appInitialState);
let appState: AppState;
store.subscribe( () => {
appState = store.getState();
observer.next(appState);
});
})
app.module.tsx
constructor(props: any) {
super(props);
DEBUG.construct && debug('Construct AppModule');
this.state = {
navigatorIsVisible: appInitialState.navigator.isVisible,
searchOverlayIsVisible: appInitialState.search.isVisible
} as State;
getAppSettings();
}
search.overlay.smart.tsx
searchOverlayIsVisible$().pipe(
takeUntil(this.destroyed$),
skip(1), // Ignore init state
)
.subscribe(searchOverlayIsVisible => {
DEBUG.subscribe && debug('Observe searchOverlayVisiblity$', searchOverlayIsVisible);
this.setState({ searchOverlayIsVisible });
this.state.searchOverlayIsVisible
});
search.overlay.service.tsx
export function toggleSearchOverlay(isVisible?: boolean) {
if (DEBUG.service && DEBUG.verbose) debug('Toggle search overlay', isVisible);
store.dispatch(
searchActions.toggleSearch(isVisible)
);
return searchOverlayIsVisible$();
}
export const searchOverlayIsVisible$ = () => store$.pipe(
map( state => SEARCH_VISIBILITY(state) ),
distinctUntilChanged()
);
Выводы
- Выдвижение начального состояния в наблюдаемую
store$
необходимо, потому что нам нужны все наблюдаемые в хранилище состояний, чтобы получить их первое состояние. Без этого начального состояния distinctUntilChanged()
не сможет выполнить сравнение между предыдущим и текущим состоянием. Если distictUntilChanged
блокирует обозримые объекты, то мы в конечном итоге блокируем ответы от webapi. Это означает, что мы видим пустые страницы, даже если хранилище состояния получило первый набор данных.
- Обратите внимание, что мы используем конструктор компонента для настройки начального состояния. Таким образом, мы используем первый цикл рендеринга для полезной работы. Второй рендеринг будет запрещен путем использования
skip(1)
во всех наблюдаемых хранилищах состояний.
- Даже если мы устанавливаем состояние инициализации в конструкторе, мы все равно сохраняем исходное состояние и в редукторах. Все действия
TOGGLE
должны начинаться с начального состояния.
- Имейте в виду, что многие процессы запускают второй рендеринг: init, маршрутизатор, обработчики событий, наблюдаемые. Пока React использует виртуальный dom для обнаружения изменений, чтобы отсеять значения, которые на самом деле не меняются, никакого реального влияния на производительность рендеринга DOM не должно быть.
- Это означает, что почти невозможно сделать только один
componentDidUpdate
вызов на изменение маршрута в LessonsPage
. Это означает, что нам все еще нужно отфильтровать повторяющиеся вызовы на handlRouteParams()
.