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

React中双日期区间渲染对象列表遇无限循环的专业解决方案

解决React日期过滤演唱会列表的无限循环问题

你的代码出现无限循环的核心原因是在组件渲染的顶层直接调用了setConcerts(filtered)。每次组件渲染时,这个状态更新函数都会执行,触发组件重新渲染,而重新渲染又会再次执行这个函数,形成无限循环。

此外还有两个潜在问题:

  • 直接修改原始concerts状态会覆盖加载回来的完整数据,导致后续无法基于全量数据重新过滤
  • 当前加载状态判断逻辑有误:初始concerts为空数组时,filtered.length也会是0,会一直显示Loading,但实际可能已经加载完成只是没有匹配数据

正确实现思路

  1. 保留原始演唱会数据状态,过滤结果作为派生状态在渲染时计算(或用useMemo优化性能)
  2. 移除顶层的setConcerts(filtered)调用,避免触发不必要的状态更新
  3. 新增加载状态变量,明确区分「正在加载」和「加载完成但无匹配数据」的场景

修改后的完整代码

import React from "react";
import axios from "axios";
import { useState, useEffect, useMemo } from "react";

const Explore = () => {
  // Raw JSON Date example:  "2023-02-08T23:15:30.000Z"
  const currentDate = new Date().toJSON().slice(0, 10);

  const [concerts, setConcerts] = useState([]);
  const [startDate, setStartDate] = useState(currentDate);
  const [endDate, setEndDate] = useState(currentDate);
  const [isLoading, setIsLoading] = useState(true); // 新增加载状态

  useEffect(() => {
    const loadConcerts = async () => {
      try {
        const request = await axios.get("data/concerts");
        setConcerts(request.data);
      } catch (err) {
        console.error("加载演唱会数据失败:", err);
      } finally {
        setIsLoading(false); // 无论成功失败都结束加载状态
      }
    };
    loadConcerts();
  }, []);

  // 使用useMemo缓存过滤结果,仅当依赖项变化时重新计算
  const filteredConcerts = useMemo(() => {
    return concerts.filter((concert) => {
      // 提取演唱会日期的YYYY-MM-DD部分,和选择的纯日期字符串匹配比较
      const concertDate = concert.datum.slice(0, 10);
      return concertDate >= startDate && concertDate <= endDate;
    });
  }, [concerts, startDate, endDate]);

  if (isLoading) {
    return <h1>Loading</h1>;
  }

  return (
    <div>
      <label htmlFor="start">Start date:</label>
      <label htmlFor="end">End date:</label>

      <input
        type="date"
        id="start"
        value={startDate}
        min="2023-01-01"
        max="2030-12-31"
        onChange={(event) => setStartDate(event.target.value)}
      />

      <input
        type="date"
        id="end"
        value={endDate}
        min="2023-01-01"
        max="2030-12-31"
        onChange={(event) => setEndDate(event.target.value)}
      />

      <div>
        {filteredConcerts.length === 0 ? (
          <p>该日期范围内没有演唱会</p>
        ) : (
          /* 这里渲染你的演唱会卡片,示例:
          filteredConcerts.map(concert => (
            <ConcertCard key={concert.id} {...concert} />
          ))
          */
          <div>渲染演唱会卡片区域</div>
        )}
      </div>
    </div>
  );
};

export default Explore;

关键修改点说明

  • 移除顶层状态更新:删掉setConcerts(filtered),不再修改原始数据状态,彻底切断无限循环的触发源
  • 新增加载状态:用isLoading变量明确标记加载阶段,解决初始空数组误判为加载中的问题
  • useMemo优化过滤:仅在concerts、startDate或endDate变化时重新计算过滤结果,避免无意义的重复计算
  • 修正日期比较逻辑:提取演唱会日期的纯日期部分再比较,确保带时间的ISO字符串和选择的日期匹配准确

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 11:35:22