React Native:如何阻止地图标记在每次状态更新时重新渲染
React Native: How to stop map markers from re-rendering on every state update
我有一个组件,它有一个地图,其中包含多个用于不同位置的自定义标记,以及一个带有用于相同位置的卡片的旋转木马。当用户按下标记时,它应该显示标注并在标记旁边显示位置名称(但在标注之外)。
但是,因为我更新了 onRegionChangeComplete
中的状态,如果用户移动地图然后快速按下标记(在 onRegionChangeComplete
中调用 setState
状态完成更新之前),那么标记将在触发 onPress
事件之前重新呈现,并且永远不会触发该事件。
一个解决方案可能是使用 shouldComponentUpdate
,但是,文档指出它应该只用于性能优化而不是防止重新渲染 (https://reactjs.org/docs/react-component.html#shouldcomponentupdate),但更重要的是,我的 componentDidUpdate
函数有一些条件逻辑依赖于 shouldComponentUpdate
中设置的区域以及其他条件操作,所以我不想阻止整个组件的重新渲染,只是不必要的重新渲染标记的渲染。
我也在使用 https://github.com/react-native-community/react-native-maps/issues/2082 中提到的性能优化,将制造商包装在实现 shouldComponentUpdate
和 getDerivedStateFromProps
的组件中,但是,我不完全确定这一点正在做任何事情,因为父组件似乎只是重新创建我所有的优化标记,而不是使用它们的优化来处理重新渲染。另外,即使我不使用包装标记而是使用传统的自定义标记,我仍然有同样的问题。
我也在 react-native-maps 上为此打开了一个问题,但还没有得到回复:https://github.com/react-native-community/react-native-maps/issues/2860
我的 'onRegionComplete' 函数在移动地图时更新状态。为了简洁起见,我删除了一些其他条件状态更新:
onRegionChangeComplete = (region) => {
const nextState = { };
nextState.region = region;
if (this.state.showNoResultsCard) {
nextState.showNoResultsCard = false;
}
.
.
.
this.setState({ ...nextState });
this.props.setSearchRect({
latitude1: region.latitude + (region.latitudeDelta / 2),
longitude1: region.longitude + (region.longitudeDelta / 2),
latitude2: region.latitude - (region.latitudeDelta / 2),
longitude2: region.longitude - (region.longitudeDelta / 2)
});
}
MapView 使用更传统的标记(不是优化版本):
<MapView // show if loaded or show a message asking for location
provider={PROVIDER_GOOGLE}
style={{ flex: 1, minHeight: 200, minWidth: 200 }}
initialRegion={constants.initialRegion}
ref={this.mapRef}
onRegionChange={this.onRegionChange}
onRegionChangeComplete={this.onRegionChangeComplete}
showsUserLocationButton={false}
showsPointsOfInterest={false}
showsCompass={false}
moveOnMarkerPress={false}
onMapReady={this.onMapReady}
customMapStyle={mapStyle}
zoomTapEnabled={false}
>
{this.state.isMapReady && this.props.places.map((place, index) => {
const calloutText = this.getDealText(place, 'callout');
return (
<Marker
tracksViewChanges
key={Shortid.generate()}
ref={(ref) => { this.markers[index] = ref; }}
coordinate={{
latitude: place.getLatitude(),
longitude: place.getLongitude()
}}
onPress={() => { this.onMarkerSelect(index); }}
anchor={{ x: 0.05, y: 0.9 }}
centerOffset={{ x: 400, y: -60 }}
calloutOffset={{ x: 8, y: 0 }}
calloutAnchor={{ x: 0.075, y: 0 }}
image={require('../../Assets/icons8-marker-80.png')}
style={index === this.state.scrollIndex ? { zIndex: 2 } : null}
>
{this.state.scrollIndex === index &&
<Text style={styles.markerTitle}>{place.getName()}</Text>}
<Callout onPress={() => this.onCalloutTap(place)} tooltip={false}>
<View style={{
borderColor: red,
width: 240,
borderWidth: 0,
borderRadius: 20,
paddingHorizontal: 8,
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'center'
}}
>
<Text style={styles.Title}>Now:</Text>
<View style={{
width: 240,
flexDirection: 'column',
justifyContent: 'space-evenly',
alignItems: 'flex-start',
paddingHorizontal: 8,
flex: 1
}}
>
{calloutText.Text}
</View>
</View>
</Callout>
</Marker>
);
})
}
</MapView>
我对标记的按下事件的函数:
onMarkerSelect(index) {
this.setState({ scrollIndex: index });
this.carousel._component.scrollToIndex({
index,
animated: true,
viewOffset: 0,
viewPosition: 0.5
});
this.markers[index].redrawCallout();
}
更新状态然后快速按下标记将导致 onPress 事件不触发。此外,每次更新父组件时,标记都是 re-rendered/recreated。 (我说重新创建是因为看起来标记正在重新渲染,甚至没有触发 shouldComponentUpdat
e 或 componentDidUpdate
)。
有没有什么方法可以在 onRegionChangeComplete
中更新状态而不强制标记重新渲染?
对于遇到此问题的任何其他人,问题是我随机生成标记的键,导致父组件在每次重新渲染时创建新标记。
具体来说,第 key={Shortid.generate()}
行是问题所在。
我有一个组件,它有一个地图,其中包含多个用于不同位置的自定义标记,以及一个带有用于相同位置的卡片的旋转木马。当用户按下标记时,它应该显示标注并在标记旁边显示位置名称(但在标注之外)。
但是,因为我更新了 onRegionChangeComplete
中的状态,如果用户移动地图然后快速按下标记(在 onRegionChangeComplete
中调用 setState
状态完成更新之前),那么标记将在触发 onPress
事件之前重新呈现,并且永远不会触发该事件。
一个解决方案可能是使用 shouldComponentUpdate
,但是,文档指出它应该只用于性能优化而不是防止重新渲染 (https://reactjs.org/docs/react-component.html#shouldcomponentupdate),但更重要的是,我的 componentDidUpdate
函数有一些条件逻辑依赖于 shouldComponentUpdate
中设置的区域以及其他条件操作,所以我不想阻止整个组件的重新渲染,只是不必要的重新渲染标记的渲染。
我也在使用 https://github.com/react-native-community/react-native-maps/issues/2082 中提到的性能优化,将制造商包装在实现 shouldComponentUpdate
和 getDerivedStateFromProps
的组件中,但是,我不完全确定这一点正在做任何事情,因为父组件似乎只是重新创建我所有的优化标记,而不是使用它们的优化来处理重新渲染。另外,即使我不使用包装标记而是使用传统的自定义标记,我仍然有同样的问题。
我也在 react-native-maps 上为此打开了一个问题,但还没有得到回复:https://github.com/react-native-community/react-native-maps/issues/2860
我的 'onRegionComplete' 函数在移动地图时更新状态。为了简洁起见,我删除了一些其他条件状态更新:
onRegionChangeComplete = (region) => {
const nextState = { };
nextState.region = region;
if (this.state.showNoResultsCard) {
nextState.showNoResultsCard = false;
}
.
.
.
this.setState({ ...nextState });
this.props.setSearchRect({
latitude1: region.latitude + (region.latitudeDelta / 2),
longitude1: region.longitude + (region.longitudeDelta / 2),
latitude2: region.latitude - (region.latitudeDelta / 2),
longitude2: region.longitude - (region.longitudeDelta / 2)
});
}
MapView 使用更传统的标记(不是优化版本):
<MapView // show if loaded or show a message asking for location
provider={PROVIDER_GOOGLE}
style={{ flex: 1, minHeight: 200, minWidth: 200 }}
initialRegion={constants.initialRegion}
ref={this.mapRef}
onRegionChange={this.onRegionChange}
onRegionChangeComplete={this.onRegionChangeComplete}
showsUserLocationButton={false}
showsPointsOfInterest={false}
showsCompass={false}
moveOnMarkerPress={false}
onMapReady={this.onMapReady}
customMapStyle={mapStyle}
zoomTapEnabled={false}
>
{this.state.isMapReady && this.props.places.map((place, index) => {
const calloutText = this.getDealText(place, 'callout');
return (
<Marker
tracksViewChanges
key={Shortid.generate()}
ref={(ref) => { this.markers[index] = ref; }}
coordinate={{
latitude: place.getLatitude(),
longitude: place.getLongitude()
}}
onPress={() => { this.onMarkerSelect(index); }}
anchor={{ x: 0.05, y: 0.9 }}
centerOffset={{ x: 400, y: -60 }}
calloutOffset={{ x: 8, y: 0 }}
calloutAnchor={{ x: 0.075, y: 0 }}
image={require('../../Assets/icons8-marker-80.png')}
style={index === this.state.scrollIndex ? { zIndex: 2 } : null}
>
{this.state.scrollIndex === index &&
<Text style={styles.markerTitle}>{place.getName()}</Text>}
<Callout onPress={() => this.onCalloutTap(place)} tooltip={false}>
<View style={{
borderColor: red,
width: 240,
borderWidth: 0,
borderRadius: 20,
paddingHorizontal: 8,
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'center'
}}
>
<Text style={styles.Title}>Now:</Text>
<View style={{
width: 240,
flexDirection: 'column',
justifyContent: 'space-evenly',
alignItems: 'flex-start',
paddingHorizontal: 8,
flex: 1
}}
>
{calloutText.Text}
</View>
</View>
</Callout>
</Marker>
);
})
}
</MapView>
我对标记的按下事件的函数:
onMarkerSelect(index) {
this.setState({ scrollIndex: index });
this.carousel._component.scrollToIndex({
index,
animated: true,
viewOffset: 0,
viewPosition: 0.5
});
this.markers[index].redrawCallout();
}
更新状态然后快速按下标记将导致 onPress 事件不触发。此外,每次更新父组件时,标记都是 re-rendered/recreated。 (我说重新创建是因为看起来标记正在重新渲染,甚至没有触发 shouldComponentUpdat
e 或 componentDidUpdate
)。
有没有什么方法可以在 onRegionChangeComplete
中更新状态而不强制标记重新渲染?
对于遇到此问题的任何其他人,问题是我随机生成标记的键,导致父组件在每次重新渲染时创建新标记。
具体来说,第 key={Shortid.generate()}
行是问题所在。