React Native演出指南APP:切换日期展示演出功能的实现问题
React Native演出指南APP日期切换问题解决方案
我正在开发一款React Native演出指南APP,Firebase集合里的每个演出文档都带date属性(格式为DD/MM/YYYY)。需求是默认展示当日演出,支持用户点击「前日演出」「次日演出」按钮切换对应日期的演出。
目前我能通过以下逻辑获取当日演出:
const day = new Date().getDate(); const month = new Date().getMonth() + 1; const year = new Date().getFullYear(); const dateToday = `${day}/${month}/${year}`; // 筛选当日演出 const gigsToday = gigs.filter((gig) => gig.date === dateToday);
但在实现日期切换时遇到了问题:
- 初始日期状态无法即时加载,导致渲染内容不符合日期条件
- 不确定操作的日期该设为普通变量还是状态变量
- 已尝试的日期切换逻辑存在错误,无法正确展示次日演出
我已经做了这些尝试:
- 将
new Date()转换为UTC+13时间:
const addHours = (numOfHours, date = new Date()) => { date.setTime(date.getTime() + numOfHours * 60 * 60 * 1000); return date; }; let localDate = addHours(13);
- 给「次日演出」按钮绑定
onPress事件,尝试更新日期状态:
<Pressable onPress={addDay}> <Text style={styles.buttonOptionsText}>next day's gigs</Text> </Pressable>
- 写了
addDay函数尝试修改日期:
const [date, setDate] = useState(dateToday); const [daysAdded, setDaysAdded] = useState(1); const addDay = () => { setDaysAdded(daysAdded + 1); localDate.setDate(localDate.getDate() + daysAdded); setDate(localDate); };
以下是完整代码:
GigMap.js
import { useState, useEffect } from "react"; import { StyleSheet, Text, View, Pressable } 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"; const GigMap = ({ navigation }) => { const [date, setDate] = useState(dateToday); const gigs = useGigs() const [daysAdded, setDaysAdded] = useState(1); const addHours = (numOfHours, date = new Date()) => { date.setTime(date.getTime() + numOfHours * 60 * 60 * 1000); return date; }; let localDate = addHours(13); useEffect(() => { setDate(localDate); }, []); const addDay = () => { setDaysAdded(daysAdded + 1); localDate.setDate(localDate.getDate() + daysAdded); setDate(localDate); }; // 生成当前日期 const day = new Date().getDate(); const month = new Date().getMonth() + 1; const year = new Date().getFullYear(); const dateToday = `${day}/${month}/${year}`; // 筛选当日演出 const gigsToday = gigs.filter((gig) => gig.date === dateToday); return ( <View style={styles.container}> <Text style={styles.headerText}>Today's gigs</Text> <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")} > <Callout style={styles.callout} onPress={() => navigation.navigate("GigDetails", { venue: gig.venue, date: gig.date, gigName: gig.gigName, time: gig.time, }) } > <CalloutView venue={gig.venue} date={gig.date} gigName={gig.gigName} time={gig.time} style={styles.calloutView} /> </Callout> </Marker> ))} </MapView> <View style={styles.buttonOptions}> <Pressable> <Text style={styles.buttonOptionsText}>previous day's gigs</Text> </Pressable> <Pressable onPress={addDay}> <Text style={styles.buttonOptionsText}>next day's gigs</Text> </Pressable> </View> </View> ); }; const styles = StyleSheet.create({ container: { flexDirection: "column", alignItems: "center", }, map: { height: 500, width: 330, margin: 10, }, headerText: { color: "black", fontSize: 20, marginTop: 5, }, callout: { width: 200, height: 100, }, buttonOptions: { flexDirection: "row", justifyContent: "flex-start", }, buttonOptionsText: { margin: 5, }, }); export default GigMap;
问题分析与解决方案
核心问题
- 日期状态管理混乱:同时使用
dateToday普通变量和date状态变量,且初始状态赋值错误(组件初始化时dateToday还未定义)。 - 日期操作逻辑错误:
addDay函数中直接修改localDate(组件渲染时创建的可变变量),并依赖daysAdded状态做累加,导致日期计算错误且无法触发正确重渲染。 - 日期格式不统一:状态中存储的是
Date对象,但筛选时用的是字符串格式,无法匹配。
修正后的代码实现
import { useState, useEffect } from "react"; import { StyleSheet, Text, View, Pressable } 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"; // 工具函数:将Date对象转换为DD/MM/YYYY格式的字符串 const formatDate = (date) => { const day = date.getDate(); const month = date.getMonth() + 1; const year = date.getFullYear(); return `${day}/${month}/${year}`; }; // 工具函数:添加指定小时数到日期(复制原对象避免副作用) const addHours = (numOfHours, date = new Date()) => { const newDate = new Date(date); newDate.setTime(newDate.getTime() + numOfHours * 60 * 60 * 1000); return newDate; }; const GigMap = ({ navigation }) => { // 状态存储当前选中的日期(Date对象) const [currentDate, setCurrentDate] = useState(null); const gigs = useGigs(); // 初始化日期:设置为UTC+13的当日 useEffect(() => { const initialDate = addHours(13); setCurrentDate(initialDate); }, []); // 切换日期:增加/减少n天 const adjustDate = (days) => { if (!currentDate) return; const newDate = new Date(currentDate); newDate.setDate(newDate.getDate() + days); setCurrentDate(newDate); }; // 筛选当前日期的演出 const filteredGigs = currentDate ? gigs.filter((gig) => gig.date === formatDate(currentDate)) : []; // 生成显示用的日期文本 const displayDateText = currentDate ? `${formatDate(currentDate)} 的演出` : "加载中..."; return ( <View style={styles.container}> <Text style={styles.headerText}>{displayDateText}</Text> <MapView initialRegion={{ latitude: -41.29416, longitude: 174.77782, latitudeDelta: 0.03, longitudeDelta: 0.03, }} style={styles.map} customMapStyle={mapStyle} > {filteredGigs.map((gig, i) => ( <Marker key={gig.id || i} // 优先用文档ID作为key,避免索引key的问题 coordinate={{ latitude: gig.location.latitude, longitude: gig.location.longitude, }} image={require("../assets/Icon_Gold_48x48.png")} > <Callout style={styles.callout} onPress={() => navigation.navigate("GigDetails", { venue: gig.venue, date: gig.date, gigName: gig.gigName, time: gig.time, }) } > <CalloutView venue={gig.venue} date={gig.date} gigName={gig.gigName} time={gig.time} style={styles.calloutView} /> </Callout> </Marker> ))} </MapView> <View style={styles.buttonOptions}> <Pressable onPress={() => adjustDate(-1)}> <Text style={styles.buttonOptionsText}>前日演出</Text> </Pressable> <Pressable onPress={() => adjustDate(1)}> <Text style={styles.buttonOptionsText}>次日演出</Text> </Pressable> </View> </View> ); }; const styles = StyleSheet.create({ container: { flexDirection: "column", alignItems: "center", }, map: { height: 500, width: 330, margin: 10, }, headerText: { color: "black", fontSize: 20, marginTop: 5, }, callout: { width: 200, height: 100, }, buttonOptions: { flexDirection: "row", justifyContent: "flex-start", }, buttonOptionsText: { margin: 5, }, }); export default GigMap;
关键优化点
- 统一日期状态:用
currentDate状态存储Date对象,所有日期操作都基于这个状态,避免变量和状态混用。 - 不可变日期操作:每次修改日期时都创建新的
Date对象,不直接修改原对象,确保React能检测到状态变化触发重渲染。 - 工具函数封装:把日期格式化、小时偏移的逻辑封装成工具函数,提高代码复用性和可读性。
- 初始化逻辑修正:在
useEffect中初始化日期,避免组件初始化时变量未定义的问题。 - 统一筛选逻辑:基于
currentDate状态生成格式化字符串,用于筛选演出,确保匹配Firestore的日期格式。 - 优化key值:优先使用演出文档的ID作为Marker的key,避免使用索引key带来的渲染问题。
内容的提问来源于stack exchange,提问作者Josh Simon
相关产品推荐
相关产品推荐

