react-native-calendars的TimelineList组件无法显示事件求助
React Native Calendars TimelineList 事件不显示问题排查
问题描述
我尝试多日渲染react-native-calendars的TimelineList日历组件,但事件项始终无法显示。代码已经变得杂乱,求解决思路。
相关代码
import React, { useState, useEffect } from "react"; import { StyleSheet, View, Text, TouchableOpacity, SafeAreaView, FlatList, } from "react-native"; import { Calendar, Agenda, Timeline, ExpandableCalendar, TimelineList, CalendarProvider, } from "react-native-calendars"; import Modal from "react-native-modal"; import FlightItem from "./FlightItem"; import UserStore from "../stores/UserStore"; import ReservationForm from "./ReservationForm"; import { db } from "../FirebaseConfig"; import { collection, addDoc, query, where, getDocs, onSnapshot, deleteDoc, doc, } from "firebase/firestore"; import { formatISO } from "date-fns"; const CalendarComponent = () => { const userDetails = UserStore; const [selectedDate, setSelectedDate] = useState( formatISO(new Date(), { representation: "date" }) ); const [isModalVisible, setIsModalVisible] = useState(false); const [selectedFlight, setSelectedFlight] = useState(null); const [editingReservation, setEditingReservation] = useState(null); const [items, setItems] = useState({ [selectedDate]: [] }); const [refresh, setRefresh] = useState(false); const userID = userDetails.userID; useEffect(() => { const currentDate = formatISO(new Date(), { representation: "date" }); if (currentDate) { loadReservations(currentDate); } const reservationsRef = collection(db, "reservations"); const q = query(reservationsRef); const unsubscribe = onSnapshot(q, (snapshot) => { const reservations = {}; snapshot.forEach((doc) => { const data = doc.data(); const date = data.date; if (!reservations[date]) { reservations[date] = []; } reservations[date].push({ id: doc.id, start: data.startTime, end: data.endTime, title: `Aircraft: ${data.selectedAircraftValue}\n${data.firstName} ${data.lastName}`, color: aircraftColors[data.selectedAircraftValue] || "#000000", }); }); console.log("Updated reservations:", reservations); setItems(reservations); }); return () => { unsubscribe(); }; }, [refresh]); const onDayPress = (day) => { if (day.dateString) { setSelectedDate(day.dateString); loadReservations(day.dateString); } }; const onBookFlight = () => { setEditingReservation(null); setIsModalVisible(true); }; const aircraftColors = { N2875Z: "#FF0000", N6351Q: "#00FF00", N67890: "#0000FF", }; const onEditReservation = (reservation) => { setEditingReservation(reservation); setIsModalVisible(true); }; const loadReservations = async (date) => { const reservationsRef = collection(db, "reservations"); const q = query(reservationsRef, where("date", "==", date)); try { const querySnapshot = await getDocs(q); const newItems = { ...items }; newItems[date] = []; await Promise.all( querySnapshot.docs.map(async (doc) => { const data = doc.data(); const { id } = doc; newItems[date].push({ id, start: data.startTime, end: data.endTime, title: `Aircraft ${data.selectedAircraftValue || "N/A"}\n${ data.firstName } ${data.lastName}`, color: aircraftColors[data.selectedAircraftValue] || "#000000", }); }) ); setItems(newItems); } catch (error) { console.error("Error fetching reservations: ", error); } }; const addOrUpdateReservation = async (date, reservation) => { const reservationData = { date, userID: userDetails.userID, firstName: userDetails.first, lastName: userDetails.last, club: userDetails.club, ...reservation, selectedAircraftValue: reservation.selectedAircraftValue, }; try { await addDoc(collection(db, "reservations"), reservationData); setIsModalVisible(false); setRefresh(!refresh); } catch (error) { console.error("Error adding reservation: ", error); } }; const handleDelete = async (id) => { try { await deleteDoc(doc(db, "reservations", id)); setIsModalVisible(false); setRefresh(!refresh); } catch (error) { console.error("Error deleting reservation: ", error); } }; const onModalClose = () => { setSelectedFlight(null); setIsModalVisible(false); setEditingReservation(null); }; return ( <SafeAreaView style={styles.container}> <CalendarProvider date={selectedDate} onDateChanged={onDayPress} showTodayButton disabledOpacity={0.6} > <ExpandableCalendar firstDay={1} /> <TimelineList events={items[selectedDate] || []} timelineProps={{ format24h: true, unavailableHours: [ { start: 0, end: 6 }, { start: 22, end: 24 }, ], overlapEventsSpacing: 8, rightEdgeSpacing: 24, }} showNowIndicator initialTime={{ hour: 9, minutes: 0 }} onEventPress={(event) => { console.log("Event pressed:", event); }} /> </CalendarProvider> <TouchableOpacity style={styles.addReservationButton} onPress={onBookFlight} > <Text style={styles.addReservationButtonText}>+</Text> </TouchableOpacity> <Modal isVisible={isModalVisible} onBackdropPress={onModalClose}> <View style={styles.modalContainer}> <Text style={styles.modalTitle}> {editingReservation ? "Edit Reservation" : "New Reservation"} </Text> <ReservationForm selectedAircraftValue={ editingReservation ? editingReservation.selectedAircraftValue : "" } onSave={(reservation) => { addOrUpdateReservation(selectedDate, reservation); }} onCancel={() => setIsModalVisible(false)} onDelete={() => handleDelete(editingReservation ? editingReservation.id : "") } editingReservation={editingReservation} editing={Boolean(editingReservation)} /> </View> </Modal> </SafeAreaView> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#F5FCFF", }, modalContainer: { backgroundColor: "white", borderRadius: 10, padding: 20, }, modalTitle: { fontSize: 18, fontWeight: "bold", marginBottom: 10, }, modalButtons: { flexDirection: "row", justifyContent: "flex-end", }, modalButton: { padding: 10, marginLeft: 10, backgroundColor: "#2196F3", borderRadius: 5, }, modalButtonText: { color: "white", fontWeight: "bold", }, emptyDataContainer: { flex: 1, alignItems: "center", justifyContent: "center", padding: 10, }, emptyDataText: { fontSize: 16, color: "#777", }, header: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingHorizontal: 20, paddingVertical: 10, backgroundColor: "#2196F3", }, headerTitle: { fontSize: 20, color: "white", fontWeight: "bold", }, headerButton: { paddingHorizontal: 10, paddingVertical: 5, borderRadius: 5, backgroundColor: "white", }, headerButtonText: { fontSize: 16, color: "#2196F3", fontWeight: "bold", }, addReservationButton: { position: "absolute", bottom: 20, right: 20, backgroundColor: "#2196F3", borderRadius: 50, width: 60, height: 60, justifyContent: "center", alignItems: "center", zIndex: 10, }, addReservationButtonText: { color: "white", fontSize: 40, }, }); export default CalendarComponent;
日志数据
Updated reservations: {"2023-04-21": [{"color": "#FF0000", "end": "17:42", "id": "3DKiauB0flrGTLQhCIRc", "start": "16:42", "title": "Aircraft: N3498G\nBob Lastname"}]}
排查思路
- 修复颜色映射缺失:日志里的飞机编号是
N3498G,但你的aircraftColors对象里只定义了3个编号,这个会导致事件颜色变成黑色,如果背景色接近黑色会完全看不到。先把N3498G: "#FF0000"加入颜色映射。 - 移除重复数据加载逻辑:
useEffect里同时调用了loadReservations和onSnapshot,两个都会修改items状态,可能导致数据被覆盖。直接删除loadReservations调用,靠onSnapshot实时同步所有数据即可。 - 硬编码事件测试组件:暂时替换
TimelineList的events属性为硬编码数组,验证组件本身是否能正常渲染事件:events={[{ id: 'test-event', start: '16:42', end: '17:42', title: '测试事件', color: '#FF0000' }]} - 检查日期格式一致性:确认
selectedDate的格式(YYYY-MM-DD)和items对象中的键完全匹配,避免因格式差异导致items[selectedDate]返回空数组。 - 验证布局空间:给
TimelineList添加style={{flex:1, backgroundColor: '#f0f0f0'}},确保它能占满日历下方的剩余空间,没有被其他组件遮挡。
内容的提问来源于stack exchange,提问作者VincentVanNo
相关产品推荐
相关产品推荐

