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

解决React Native中日期选择时重复调用getOpenHours的问题

解决方案

核心问题分析

  1. 原代码中handleDateChange未更新全局的selectedDate状态,导致无法判断当前选择的日期是否和上一次一致
  2. react-native-modern-datepicker的onSelectedChange可能在组件重渲染、日期选中状态变化时重复触发,未做拦截处理

具体修改步骤

1. 同步选中日期状态,增加重复请求拦截

修改handleDateChange函数,先格式化日期并和当前状态对比,相同则直接返回,避免重复执行请求:

const handleDateChange = async (date) => {
  // 格式化日期为统一格式(和状态存储格式一致)
  const formattedDate = date.replace(/\//g, "-");
  
  // 对比当前选中日期,相同则直接返回,不执行后续逻辑
  if (selectedDate === formattedDate) return;

  // 更新全局选中日期状态
  setSelectedDate(formattedDate);
  
  // 转换为Date对象用于请求
  const dateObj = new Date(formattedDate);
  const hours = await getOpeningHours(dateObj);
  
  setOpenHours(hours);
  setSelectedStartHour(null);
  setSelectedEndHour(null);
  setStartHour(null);
  setEndHour(null);
};

2. 给请求结果加缓存(可选但推荐)

如果同一个日期可能被多次选中,用缓存存储已请求过的结果,避免重复发起网络请求:

// 用useRef存储缓存,避免组件重渲染时重置
const hoursCache = useRef({});

const getOpeningHours = async (date) => {
  // 把Date对象转为字符串作为缓存key
  const dateKey = date.toISOString().split("T")[0];
  
  // 如果缓存中有结果,直接返回
  if (hoursCache.current[dateKey]) {
    return hoursCache.current[dateKey];
  }

  try {
    const response = await axios.post(
      `https://spacezone-backend.cyclic.app/api/booking/getOpenHours/${placeId}`,
      { Date: date }
    );
    // 存入缓存
    hoursCache.current[dateKey] = response.data.openHoursArray;
    return response.data.openHoursArray;
  } catch (error) {
    console.log(error);
    return [];
  }
};

3. 修复日期显示的小问题

原代码中<Text>Selected Date is {selectedDate}</Text>因为之前没更新状态,一直显示null,现在同步状态后就能正常显示选中的日期了。


修改后的完整代码片段

const RoomDetailsPage = ({ route }) => {
  const [selectedDate, setSelectedDate] = useState(null);
  const [selectedStartHour, setSelectedStartHour] = useState(null);
  const [selectedEndHour, setSelectedEndHour] = useState(null);
  const [startHour, setStartHour] = useState(null);
  const [endHour, setEndHour] = useState(null);
  const [isDatePickerVisible, setDatePickerVisible] = useState(false);
  const { roomId, placeId, roomDetails } = route.params;
  const startDate = roomDetails.days[0].date.split("T")[0];
  const endDate = roomDetails.days[roomDetails.days.length - 1].date.split("T")[0];
  const [openHours, setOpenHours] = useState([]);
  
  // 缓存已请求过的日期的开放时间
  const hoursCache = useRef({});

  const handleDateChange = async (date) => {
    const formattedDate = date.replace(/\//g, "-");
    
    // 拦截重复日期选择
    if (selectedDate === formattedDate) return;

    setSelectedDate(formattedDate);
    const dateObj = new Date(formattedDate);
    const hours = await getOpeningHours(dateObj);
    
    setOpenHours(hours);
    setSelectedStartHour(null);
    setSelectedEndHour(null);
    setStartHour(null);
    setEndHour(null);
  };

  const getOpeningHours = async (date) => {
    const dateKey = date.toISOString().split("T")[0];
    
    if (hoursCache.current[dateKey]) {
      return hoursCache.current[dateKey];
    }

    try {
      const response = await axios.post(
        `https://spacezone-backend.cyclic.app/api/booking/getOpenHours/${placeId}`,
        { Date: date }
      );
      hoursCache.current[dateKey] = response.data.openHoursArray;
      return response.data.openHoursArray;
    } catch (error) {
      console.log(error);
      return [];
    }
  };

  return (
    <View>
      <View>
        <Text style={styles.subtitle}>Select Date</Text>
        <Text>Selected Date is {selectedDate}</Text>
        <DatePicker
          onSelectedChange={handleDateChange}
          options={{
            backgroundColor: "#090C08",
            textHeaderColor: "#FFA25B",
            textDefaultColor: "#F6E7C1",
            selectedTextColor: "#fff",
            mainColor: "#F4722B",
            textSecondaryColor: "#D6C7A1",
            borderColor: "rgba(122, 146, 165, 0.1)",
          }}
          current="2023-06-01"
          mode="calendar"
          minimumDate={startDate}
          maximumDate={endDate}
          minuteInterval={30}
          style={{ borderRadius: 10 }}
        />
      </View>

      {/* List of selectable hours */}
      {openHours && openHours.length > 0 ? (
        <>
          <Text style={styles.subtitle}>Select Hours</Text>
          <View style={styles.hoursContainer}>
            {openHours.map((hour) => (
              <TouchableOpacity
                key={hour}
                style={[
                  styles.hourCard,
                  hour === selectedStartHour || hour === selectedEndHour
                    ? styles.selectedHourCard
                    : null,
                ]}
                onPress={() => handleHourPress(hour)}
              >
                <Text>{hour}:00</Text>
              </TouchableOpacity>
            ))}
          </View>
        </>
      ) : (
        <Text>Loading open hours...</Text>
      )}
    </View>
  );
};

关键说明

  • 日期对比拦截是核心:确保只有当日期真正变化时才执行请求和状态更新
  • 缓存机制进一步优化性能:避免相同日期重复发起网络请求,减少服务器压力和用户等待时间
  • 同步selectedDate状态:既解决了重复请求的判断依据,也修复了页面上日期显示的问题

内容的提问来源于stack exchange,提问作者YoussefAdel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 02:47:06