You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,在切换日期时调用官方提供的关闭方法:

  1. 创建MapView的ref:
const mapRef = useRef(null);
  1. 给MapView绑定ref:
<MapView
  ref={mapRef}
  // 其他原有属性保持不变
>
  {/* Marker代码 */}
</MapView>
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 19:35:24