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

React动态路由用户详情页刷新时API数据未加载致页面崩溃

问题描述

我正在开发React项目,已实现从外部API拉取用户信息并在UI端通过无限滚动加载展示列表的功能,经查阅资料完成了无限滚动逻辑的编写。
目前我正在实现动态路由功能:用户点击承载用户信息的卡片后,将跳转至该用户的个人详情页,通过另一个API接口拉取并展示该用户的更多个人信息,目前路由跳转逻辑已调试完成。
但我遇到了异常问题:当处于单个用户的详情页URL下,请求拉取该用户的详细个人信息API时出现异常,具体表现如下,先附上我的相关代码:

相关代码

Home.jsx(首页列表+无限滚动逻辑)

function Home() {
  const [items, setItems] = useState([]);
  const [isFetching, setIsFetching] = useState(false);
  const [page, setPage] = useState(1);

  const fetchData = (setItems, items) => {
   let url =  `http://sweeftdigital-intern.eu-central-1.elasticbeanstalk.com/user/1/50`;
   axios.get(url).then((res) => {
       setItems([...items, ...res.data.list]);
       console.log(res);
     });
  };

  const moreData = () => {
    let url = `http://sweeftdigital-intern.eu-central-1.elasticbeanstalk.com/user/${page}/50`;
    axios.get(url).then(res => {
      setItems([...items, ...res.data.list]);
      setPage(page+1)
      setIsFetching(false)
    });
  }

  const isScrolling =()=>{
    if(window.innerHeight + document.documentElement.scrollTop!==document.documentElement.offsetHeight){
      return;
    }
    setIsFetching(true)
  }
 
useEffect(()=>{
   fetchData(setItems,items);
   window.addEventListener("scroll", isScrolling);
    return () => window.removeEventListener("scroll", isScrolling);
 },[]);


 useEffect(() => {
  if (isFetching){
    moreData();
  }
}, [isFetching]);


  return (
    <div className="card-container">
       {items.map((user,key) => (
        <Card key={key} id={user.id} prefix={user.prefix} name={user.name} lastName={user.lastName} image={user.imageUrl} job={user.title}/>
       ))}
     </div>
  );
}

以上是主路径文件Home.jsx的代码,我在该文件中实现无限滚动逻辑,遍历渲染从API获取的用户数组状态;点击用户卡片后将跳转至Users.jsx对应的详情路由,代码如下:

Users.jsx(用户详情页)

export default function Users() {
  const params = useParams();
  const [users, setUsers] = useState({})


  useEffect(()=>{
    async function fetchData(){
      const res = await axios(`http://sweeftdigital-intern.eu-central-1.elasticbeanstalk.com/user/${params.id}`);
      console.log(res.data);
      setUsers(res.data);

    }
    fetchData()
  },[])

  return (
    <div className='container'>
      <img src={users.imageUrl}/>
      <h1 className=''>Info</h1>
      <div className='card-div'>
        <div className='name-div'>
          <h3 style={{fontWeight:"bold", fontSize:"25px"}}>{users.prefix} {users.name} {users.lastName}</h3>
          <h3 style={{fontStyle:"italic", fontSize:"20px"}}>{users.title}</h3>
        </div>
        <div className='details-div'>
          <h3>Email: {users.email}</h3>
          <h3>Ip Address: {users.ip}</h3>
          <h3>Job Area: {users.jobArea}</h3>
          <h3>Job Type: {users.jobType}</h3>
        </div>
      </div>
      <h1 className='adress-h1'>Address</h1>
      <div className='address-div'>
        <h3>{users.company.name} {users.company.suffix}</h3>
        <h3>City: {users.address.city}</h3>
        <h3>Country: {users.address.country}</h3>
        <h3>State: {users.address.state}</h3>
        <h3>Street Address: {users.address.streetAddress}</h3>
        <h3>ZIP: {users.address.zipCode}</h3>
      </div>
    </div>
  )
}

异常表现

  • 如果我先注释掉Users.jsx中类名为container的div内的渲染代码,接口请求功能可正常运行,控制台能正常打印返回的数据;此时取消注释,页面可正常渲染并正确展示API返回的用户数据。
  • 但如果我在详情页直接刷新页面,页面内容会消失、页面崩溃并抛出多个TypeError。
  • 我推测是页面首次渲染时API返回的数据还未加载完成,导致访问不存在的属性引发报错,但不知道具体修复方案。

问题原因

你的判断没错。
你给users设置的初始值是空对象{},组件第一次渲染的时候接口请求还没返回,这时候直接访问users.company.name、users.address.city这类嵌套属性,等价于从undefined上读取属性——空对象上没有company字段,读出来是undefined,再往下读name直接就抛TypeError,页面直接崩。
之前从首页点进详情页不报错,是开发环境React严格模式会触发两次渲染,第一次报错后第二次渲染时接口数据刚好返回,你没察觉到异常;但直接在详情页刷新,首次渲染必然拿到空对象,直接触发崩溃。

修复方案

选任意一种即可,推荐组合使用:

  1. 加加载状态兜底:新增loading状态,请求没完成的时候先展示加载提示,不要直接渲染用户信息。
    改Users.jsx的逻辑:
    export default function Users() {
      const params = useParams();
      // 初始值设为null更方便判断加载状态
      const [users, setUsers] = useState(null)
      const [loading, setLoading] = useState(true)
    
      useEffect(()=>{
        async function fetchData(){
          try {
            const res = await axios(`http://sweeftdigital-intern.eu-central-1.elasticbeanstalk.com/user/${params.id}`);
            setUsers(res.data);
          } catch (err) {
            console.error('请求失败', err)
          } finally {
            // 不管成功失败都关掉loading
            setLoading(false)
          }
        }
        fetchData()
      },[params.id]) // 补全params.id依赖,不然用户id变化时不会重新拉取数据
    
      // 加载中兜底
      if (loading) return <div>加载中...</div>
      // 请求失败兜底
      if (!users) return <div>用户信息加载失败</div>
    
      return (
        <div className='container'>
          <img src={users.imageUrl}/>
          <h1 className=''>Info</h1>
          <div className='card-div'>
            <div className='name-div'>
              <h3 style={{fontWeight:"bold", fontSize:"25px"}}>{users.prefix} {users.name} {users.lastName}</h3>
              <h3 style={{fontStyle:"italic", fontSize:"20px"}}>{users.title}</h3>
            </div>
            <div className='details-div'>
              <h3>Email: {users.email}</h3>
              <h3>Ip Address: {users.ip}</h3>
              <h3>Job Area: {users.jobArea}</h3>
              <h3>Job Type: {users.jobType}</h3>
            </div>
          </div>
          <h1 className='adress-h1'>Address</h1>
          <div className='address-div'>
            {/* 嵌套属性加可选链兜底,避免后端返回字段缺失时报错 */}
            <h3>{users.company?.name} {users.company?.suffix}</h3>
            <h3>City: {users.address?.city}</h3>
            <h3>Country: {users.address?.country}</h3>
            <h3>State: {users.address?.state}</h3>
            <h3>Street Address: {users.address?.streetAddress}</h3>
            <h3>ZIP: {users.address?.zipCode}</h3>
          </div>
        </div>
      )
    }
    
  2. 用可选链操作符做属性访问兜底:所有访问嵌套属性的地方加?.,比如users?.company?.name,就算前面的值是undefined也不会报错,只会返回undefined,页面不会崩溃。

额外提醒:你首页的无限滚动逻辑存在闭包问题,moreData方法里拿到的items和page始终是首次渲染的旧值,滚动加载多了会出现重复数据、页码错乱的问题,修复时把setItems和setPage改成函数式更新即可,比如setItems(prev => [...prev, ...res.data.list])、setPage(prev => prev+1),不要直接依赖外部的items、page变量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 13:06:22