React预约日历项目报错:Cannot read properties of undefined (reading 'appointments')
解决「Cannot read properties of undefined (reading 'appointments')」错误及实现日期预约功能
错误根源
你遇到的错误是因为useQuery在请求未完成(loading为true)或请求失败时,data变量为undefined,此时直接访问data.appointments会触发属性读取错误。另外当前代码还存在逻辑偏差:会渲染所有预约,而非你选中日期的对应预约。
修复步骤及代码修改
1. 处理加载与错误状态
在组件渲染时优先判断loading和error状态,避免在数据未就绪时访问data:
- 加载中显示提示文本
- 请求失败时展示错误信息
2. 安全访问data.appointments
使用可选链操作符(?.)或多层条件判断,确保data存在后再访问appointments属性。
3. 过滤选中日期的预约
添加日期匹配逻辑,只渲染与selectedDate对应的预约项,需统一日期格式后再做对比。
修改后的完整代码
import React, { useState } from "react"; import { GET_APPOINTMENTS, CREATE_APPOINTMENT } from "../utils/mutations"; import { useMutation, useQuery } from "@apollo/client"; import Calendar from "react-calendar"; function AppointmentCalendar() { const { loading, error, data } = useQuery(GET_APPOINTMENTS); const [createAppointment] = useMutation(CREATE_APPOINTMENT, { // 新增:创建预约后自动重新拉取列表,更新UI refetchQueries: [{ query: GET_APPOINTMENTS }] }); const [date, setDate] = useState(new Date()); const [selectedDate, setSelectedDate] = useState(null); const [appointmentText, setAppointmentText] = useState(""); const handleDateChange = (date) => { setSelectedDate(date); }; const handleAddAppointment = () => { if (selectedDate && appointmentText.trim() !== "") { createAppointment({ variables: { date: selectedDate.toISOString(), text: appointmentText }, }) .then(() => { setSelectedDate(null); setAppointmentText(""); }) .catch((error) => { console.error(error); }); } }; // 辅助函数:格式化日期为YYYY-MM-DD,用于匹配预约日期 const formatDate = (date) => { return date.toISOString().split('T')[0]; }; // 加载状态拦截 if (loading) return <div>加载预约数据中...</div>; // 错误状态拦截 if (error) return <div>获取预约失败: {error.message}</div>; return ( <div> <h1>预约日历</h1> <div> <Calendar onChange={handleDateChange} value={date} /> </div> <div> {selectedDate && ( <div> <h2>{selectedDate.toLocaleDateString()} 的预约</h2> <input type="text" placeholder="添加预约内容" value={appointmentText} onChange={(e) => setAppointmentText(e.target.value)} /> <button onClick={handleAddAppointment}>添加预约</button> {/* 安全访问并过滤选中日期的预约 */} {data?.appointments?.length > 0 ? ( <ul> {data.appointments .filter(appt => formatDate(new Date(appt.date)) === formatDate(selectedDate)) .map((appointment) => ( <li key={appointment.id}>{appointment.text}</li> ))} </ul> ) : ( <p>当前日期暂无预约</p> )} </div> )} </div> </div> ); } export default AppointmentCalendar;
额外优化说明
- 新增
refetchQueries配置,创建预约后自动重新拉取列表,确保UI实时同步 - 添加日期格式化辅助函数,保证选中日期与预约日期的匹配逻辑准确
- 当选中日期无预约时,显示友好提示文本
内容的提问来源于stack exchange,提问作者willisN
相关产品推荐
相关产品推荐

