mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Maps] fix maps listing page 'Cannot update a component while rendering a different component' (#155545)
To view problem
1) Install sample data set
2) Open maps listing page. Notice the following console warning
<img width="400" alt="Screen Shot 2023-04-21 at 9 19 16 AM"
src="https://user-images.githubusercontent.com/373691/233705813-75efc248-d0e4-40da-bc26-aa01b2e21b00.png">
PR removes side effect during `MapsListViewComp` render that caused
[ScreenReaderRouteAnnouncements](0cd7973e1f/packages/core/chrome/core-chrome-browser-internal/src/ui/header/screen_reader_a11y.tsx
)
to update state.
PR also refactors LoadListAndRender into a functional component with
hooks. That change is not needed but was done to isolate the issue.
Figured its worth keeping.
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
085686fceb
commit
d4f2639377
5 changed files with 42 additions and 51 deletions
|
@ -5,12 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public';
|
||||
import { ScopedHistory } from '@kbn/core/public';
|
||||
import { getToasts } from '../../kibana_services';
|
||||
import { MapsListView } from './maps_list_view';
|
||||
import { APP_ID } from '../../../common/constants';
|
||||
import { mapsClient } from '../../content_management';
|
||||
|
@ -20,49 +18,39 @@ interface Props {
|
|||
stateTransfer: EmbeddableStateTransfer;
|
||||
}
|
||||
|
||||
export class LoadListAndRender extends Component<Props> {
|
||||
_isMounted: boolean = false;
|
||||
state = {
|
||||
mapsLoaded: false,
|
||||
hasSavedMaps: null,
|
||||
};
|
||||
export function LoadListAndRender(props: Props) {
|
||||
const [mapsLoaded, setMapsLoaded] = useState(false);
|
||||
const [hasSavedMaps, setHasSavedMaps] = useState(true);
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
this.props.stateTransfer.clearEditorState(APP_ID);
|
||||
this._loadMapsList();
|
||||
useEffect(() => {
|
||||
props.stateTransfer.clearEditorState(APP_ID);
|
||||
|
||||
let ignore = false;
|
||||
mapsClient
|
||||
.search({ limit: 1 })
|
||||
.then((results) => {
|
||||
if (!ignore) {
|
||||
setHasSavedMaps(results.hits.length > 0);
|
||||
setMapsLoaded(true);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (!ignore) {
|
||||
setMapsLoaded(true);
|
||||
setHasSavedMaps(false);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
ignore = true;
|
||||
};
|
||||
// only run on mount
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!mapsLoaded) {
|
||||
// do not render loading state to avoid UI flash when listing page is displayed
|
||||
return null;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
async _loadMapsList() {
|
||||
try {
|
||||
const results = await mapsClient.search({ limit: 1 });
|
||||
if (this._isMounted) {
|
||||
this.setState({ mapsLoaded: true, hasSavedMaps: !!results.hits.length });
|
||||
}
|
||||
} catch (err) {
|
||||
if (this._isMounted) {
|
||||
this.setState({ mapsLoaded: true, hasSavedMaps: false });
|
||||
getToasts().addDanger({
|
||||
title: i18n.translate('xpack.maps.mapListing.errorAttemptingToLoadSavedMaps', {
|
||||
defaultMessage: `Unable to load maps`,
|
||||
}),
|
||||
text: `${err}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { mapsLoaded, hasSavedMaps } = this.state;
|
||||
|
||||
if (mapsLoaded) {
|
||||
return hasSavedMaps ? <MapsListView history={this.props.history} /> : <Redirect to="/map" />;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return hasSavedMaps ? <MapsListView history={props.history} /> : <Redirect to="/map" />;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback, memo } from 'react';
|
||||
import React, { useCallback, memo, useEffect } from 'react';
|
||||
import type { SavedObjectsFindOptionsReference, ScopedHistory } from '@kbn/core/public';
|
||||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -72,8 +72,14 @@ function MapsListViewComp({ history }: Props) {
|
|||
const listingLimit = getUiSettings().get(SAVED_OBJECTS_LIMIT_SETTING);
|
||||
const initialPageSize = getUiSettings().get(SAVED_OBJECTS_PER_PAGE_SETTING);
|
||||
|
||||
getCoreChrome().docTitle.change(APP_NAME);
|
||||
getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]);
|
||||
// TLDR; render should be side effect free
|
||||
//
|
||||
// setBreadcrumbs fires observables which cause state changes in ScreenReaderRouteAnnouncements.
|
||||
// wrap chrome updates in useEffect to avoid potentially causing state changes in other component during render phase.
|
||||
useEffect(() => {
|
||||
getCoreChrome().docTitle.change(APP_NAME);
|
||||
getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]);
|
||||
}, []);
|
||||
|
||||
const findMaps = useCallback(
|
||||
async (
|
||||
|
|
|
@ -20560,7 +20560,6 @@
|
|||
"xpack.maps.mapActions.removeFeatureError": "Impossible de retirer la fonctionnalité de l’index.",
|
||||
"xpack.maps.mapListing.entityName": "carte",
|
||||
"xpack.maps.mapListing.entityNamePlural": "cartes",
|
||||
"xpack.maps.mapListing.errorAttemptingToLoadSavedMaps": "Impossible de charger les cartes",
|
||||
"xpack.maps.mapSavedObjectLabel": "Carte",
|
||||
"xpack.maps.mapSettingsPanel.addCustomIcon": "Ajouter une icône personnalisée",
|
||||
"xpack.maps.mapSettingsPanel.autoFitToBoundsLocationLabel": "Ajuster automatiquement la carte aux limites de données",
|
||||
|
|
|
@ -20560,7 +20560,6 @@
|
|||
"xpack.maps.mapActions.removeFeatureError": "インデックスから機能を削除できません。",
|
||||
"xpack.maps.mapListing.entityName": "マップ",
|
||||
"xpack.maps.mapListing.entityNamePlural": "マップ",
|
||||
"xpack.maps.mapListing.errorAttemptingToLoadSavedMaps": "マップを読み込めません",
|
||||
"xpack.maps.mapSavedObjectLabel": "マップ",
|
||||
"xpack.maps.mapSettingsPanel.addCustomIcon": "カスタムアイコンを追加",
|
||||
"xpack.maps.mapSettingsPanel.autoFitToBoundsLocationLabel": "自動的にマップをデータ境界に合わせる",
|
||||
|
|
|
@ -20560,7 +20560,6 @@
|
|||
"xpack.maps.mapActions.removeFeatureError": "无法从索引中移除特征。",
|
||||
"xpack.maps.mapListing.entityName": "地图",
|
||||
"xpack.maps.mapListing.entityNamePlural": "地图",
|
||||
"xpack.maps.mapListing.errorAttemptingToLoadSavedMaps": "无法加载地图",
|
||||
"xpack.maps.mapSavedObjectLabel": "地图",
|
||||
"xpack.maps.mapSettingsPanel.addCustomIcon": "添加定制图标",
|
||||
"xpack.maps.mapSettingsPanel.autoFitToBoundsLocationLabel": "使地图自动适应数据边界",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue