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

React组件挂载早于API响应,舞蹈数据无法渲染问题排查

问题:API获取舞蹈数据前useEffect已执行,导致数据无法正常显示

我希望展示通过API调用获取的舞蹈数据,但useEffect在API返回响应前就已执行,导致数据无法正常显示。已附上代码,希望了解问题所在及此类场景的最佳实践,不想在函数内直接返回HTML。

function Home() {
  const userId = useSelector((state: RootState) => state.user.id);
  const [dances, setDances] = useState<DanceCardProps[]>([])
  interface DanceCardProps {
    picture?: string;
    name: string;
    danceId: string;
    description?: string;
    author: string;
    year?: number;
  }

  const fetchDanceData = async() => {
    axios.get(process.env.REACT_APP_LOCAL_API_URL + "/app/getDances?accountId=1", {
      headers: {EXAMPLE,
      },
    })
      .then((res) => {
        console.log("res: ", res);
        var newArray = dances as DanceCardProps[]
        res.data.map((obj: DanceCardProps )=> {
          console.log(obj)
          const newDance = {
            danceId: obj.danceId,
            name: obj.name,
            author: obj.author
          }
          newArray.push(newDance)
        })
        setDances(newArray as DanceCardProps[])
        console.log("dances: ",dances)
      })
      .catch((e) => {
        console.log("error: ", e);
      });
  };


  useEffect(function(){
    fetchDanceData()
  },[dances])

  return (
    <>
      <Stack className="wrapper">
        <FilterRow func={fetchDanceData} />
        <Divider />
        <Grid container spacing={3} className="home-grid" direction="row">
          {dances.map((dance: DanceCardProps) => {
            return (
              <DanceCard
                name={dance.name}
                id={dance.danceId}
                author={dance.author}
                year={dance.year}
              />
            );
          })}
        </Grid>
      </Stack>
    </>
  );
}

日志截图


问题分析与解决方案

核心问题

  1. useEffect依赖错误:将dances作为依赖项,会导致每次状态更新都触发重新请求,形成无限循环;且初始请求是异步的,组件会先渲染空数组,这是正常流程,但后续状态更新逻辑出错导致数据无法展示。
  2. 状态更新违反不可变原则:直接修改原状态数组(newArray = dances后push),React无法检测到状态引用变化,因此不会触发组件重新渲染展示新数据。另外,console.log(dances)打印的是旧状态,因为setState是异步操作。

修复后的代码

function Home() {
  const userId = useSelector((state: RootState) => state.user.id);
  const [dances, setDances] = useState<DanceCardProps[]>([])
  
  interface DanceCardProps {
    picture?: string;
    name: string;
    danceId: string;
    description?: string;
    author: string;
    year?: number;
  }

  const fetchDanceData = async() => {
    try {
      const res = await axios.get(`${process.env.REACT_APP_LOCAL_API_URL}/app/getDances?accountId=1`, {
        headers: { EXAMPLE },
      });
      console.log("res: ", res);
      // 基于响应数据创建新数组,不修改原状态
      const newDances = res.data.map((obj: DanceCardProps )=> ({
        danceId: obj.danceId,
        name: obj.name,
        author: obj.author,
        year: obj.year
      }));
      setDances(newDances);
    } catch (e) {
      console.log("error: ", e);
    }
  };

  // 仅组件挂载时执行一次,若需根据用户切换重新请求,可添加userId到依赖数组
  useEffect(() => {
    fetchDanceData();
  }, []);

  return (
    <>
      <Stack className="wrapper">
        <FilterRow func={fetchDanceData} />
        <Divider />
        <Grid container spacing={3} className="home-grid" direction="row">
          {dances.map((dance: DanceCardProps) => (
            <DanceCard
              key={dance.danceId} // 添加唯一key,避免React渲染警告
              name={dance.name}
              id={dance.danceId}
              author={dance.author}
              year={dance.year}
            />
          ))}
        </Grid>
      </Stack>
    </>
  );
}

此类场景的最佳实践

  • 精准设置useEffect依赖:仅在组件挂载时请求数据用空依赖数组[];若需在特定变量(如userId)变化时重新请求,将该变量加入依赖数组。
  • 严格遵循状态不可变:更新数组/对象时,通过map、扩展运算符[...arr]等方式创建新引用,禁止直接修改原状态。
  • 用async/await简化异步逻辑:替代then/catch让代码更易读,同时统一处理请求错误。
  • 添加加载状态(可选):API请求较慢时,通过isLoading状态展示加载提示,提升用户体验:
    const [isLoading, setIsLoading] = useState(true);
    
    const fetchDanceData = async() => {
      setIsLoading(true);
      try {
        // 请求逻辑
        setDances(newDances);
      } catch (e) {
        // 错误处理
      } finally {
        setIsLoading(false);
      }
    };
    
    // 在return中优先判断加载状态
    if (isLoading) return <div>加载中...</div>;
    
  • 列表项必须加唯一key:这是React列表渲染的强制要求,能避免渲染警告并提升性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:53:11