React Native Maps标记属性随状态变更未更新问题求助
问题描述
我用React Native和react-native-maps开发演出指南应用,首页地图展示当日演出标记,点击“Next day's gigs”按钮可切换至次日演出,地图会渲染新标记。但点击标记弹出的演出信息存在问题:切换日期后,弹窗内的数据并未更新,仍显示前一天的内容。奇怪的是标记的coordinate属性能正常更新,但description和title属性却不会:
{gigsToday.map((gig, i) => ( <Marker key={i} coordinate={{ latitude: gig.location.latitude, // <--- 此处正常更新 longitude: gig.location.longitude, // <--- 此处正常更新 }} image={require("../assets/Icon_Gold_48x48.png")} description={gig.venue} // <--- 此处未正常更新 title={gig.gigName} //<--- 此处未正常更新 /> ))} </MapView>
请问如何修复该问题,使标记信息随数据变更同步更新?或者至少在用户切换日期时关闭弹窗?
完整组件代码(GigMaps.js)
import { useState, useMemo, useEffect } from "react"; import { StyleSheet, Text, View, Pressable, Image, TouchableOpacity, } from "react-native"; import MapView from "react-native-maps"; import { Marker, Callout } from "react-native-maps"; import CalloutView from "./CalloutView"; import { mapStyle } from "../util/mapStyle"; import { useGigs } from "../hooks/useGigs"; import { AntDesign } from "@expo/vector-icons"; const GigMap = ({ navigation }) => { const [selectedDateMs, setSelectedDateMs] = useState(Date.now()); const [ currentDayGigs,setCurrentDayGigs ] = useState([]) const gigs = useGigs(); //generates current date in format DD/MM/YYYY const selectedDateString = useMemo(() => { const date = new Date(selectedDateMs); const dateToString = date.toString().slice(0,15) return dateToString // returns in form 'Tue Dec 20 2022' }, [selectedDateMs]); //Filtering through gigs to return only current day's gigs const gigsToday = gigs.filter((gig) => { const gigDate1 = new Date(gig.dateAndTime.seconds*1000) const gigDate2 = gigDate1.toString().slice(0,15) //return form 'Tue Dec 20 2022' return gigDate2 === selectedDateString }) // const generateGigsToday = () => { // useEffect(() => { // const gigsToday = gigs.filter((gig) => { // const gigDate1 = new Date(gig.dateAndTime.seconds*1000) // const gigDate2 = gigDate1.toString().slice(0,15) //return form 'Tue Dec 20 2022' // return gigDate2 === selectedDateString // }) // setCurrentDayGigs(gigsToday) // },[selectedDateMs]) // } // generateGigsToday() // const gigsToday = useMemo(() => { // return ( // gigs.filter((gig) => { // const gigDate1 = new Date(gig.dateAndTime.seconds*1000) // const gigDate2 = gigDate1.toString().slice(0,15) // return gigDate2 === selectedDateString // }) // ) // },[selectedDateMs]) //increments date by amount const addDays = (amount) => { setSelectedDateMs((curr) => curr + 1000 * 60 * 60 * 24 * amount); }; return ( <View style={styles.container}> <Text style={styles.headerText}>{`Gigs on ${selectedDateString}`}</Text> <View style={styles.imageText}> <Text style={styles.subHeader}>Tap on</Text> <Image style={styles.image} source={require("../assets/Icon_Gold_48x48.png")} /> <Text style={styles.subHeader}> to see gig info</Text> </View> <MapView initialRegion={{ latitude: -41.29416, longitude: 174.77782, latitudeDelta: 0.03, longitudeDelta: 0.03, }} style={styles.map} customMapStyle={mapStyle} > {gigsToday.map((gig, i) => ( <Marker key={i} coordinate={{ latitude: gig.location.latitude, longitude: gig.location.longitude, }} image={require("../assets/Icon_Gold_48x48.png")} description={gig.venue} title={gig.gigName} /> ))} </MapView> <View style={styles.buttonOptions}> <TouchableOpacity onPress={() => addDays(-1)} style={styles.touchable}> <AntDesign name="caretleft" size={36} color="#778899" /> <Text style={{fontFamily:'Helvetica-Neue', color:'#778899'}}>Previous day's gigs</Text> </TouchableOpacity> <TouchableOpacity onPress={() => addDays(1)} style={styles.touchable}> <AntDesign name="caretright" size={36} color="#778899" /> <Text style={{fontFamily:'Helvetica-Neue',color:'#778899'}}>Next day's gigs</Text> </TouchableOpacity> </View> </View> ); };
解决方案
方法1:用useMemo缓存过滤结果,确保Marker属性同步更新
当前代码中gigsToday直接在渲染阶段执行filter,没有依赖缓存,React无法精准检测数据变化,导致Marker的title/description未更新。改用useMemo缓存过滤结果,绑定依赖项selectedDateMs和gigs:
const gigsToday = useMemo(() => { return gigs.filter((gig) => { const gigDate1 = new Date(gig.dateAndTime.seconds * 1000) const gigDate2 = gigDate1.toString().slice(0,15) return gigDate2 === selectedDateString }) }, [selectedDateMs, gigs])
这样每次selectedDateMs或gigs变化时,gigsToday会重新计算,Marker的props会正确触发更新。
方法2:切换日期时强制关闭弹窗
如果只需要确保切换日期时弹窗关闭,可给MapView添加ref,在切换日期时调用官方提供的关闭方法:
- 创建MapView的ref:
const mapRef = useRef(null);
- 给MapView绑定ref:
<MapView ref={mapRef} // 其他原有属性保持不变 > {/* Marker代码 */} </MapView>
- 修改
addDays方法,添加关闭弹窗的逻辑:
const addDays = (amount) => { mapRef.current?.deselectAllCallouts(); setSelectedDateMs((curr) => curr + 1000 * 60 * 60 * 24 * amount); };
此方法能确保切换日期时所有打开的弹窗都关闭,用户再次点击新标记时会显示当日的正确演出信息。
额外优化:使用唯一key提升渲染准确性
当前用数组索引i作为Marker的key,当演出列表顺序变化时可能导致React渲染异常。建议改用演出对象的唯一标识符作为key,比如:
<Marker key={gig.id} // 替换为gig对象中实际的唯一ID字段 // 其他属性不变 />
这样React能更准确地识别每个Marker的变化,避免不必要的渲染问题。
内容的提问来源于stack exchange,提问作者Josh Simon
相关产品推荐
相关产品推荐

