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

React升级后未登录用户访问页面报错及路由跳转问题

解决React Scripts升级到5.0后Userinfo页面未登录跳转报错问题

问题核心

未登录时user为null,但useFetch会先执行并尝试读取user.contractType导致报错;直接把user判断放在useFetch前又违反React Hooks不能条件调用的规则。以下是两种合规的解决方法:


方案一:给useFetch添加条件触发开关

给自定义的useFetch钩子增加enabled参数,仅当参数为true时发起请求,同时用可选链避免URL拼接报错:

import { useContext, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import useFetch from './useFetch';
import AuthContext from './AuthContext';

const UserInfoPage = () => {
  const user = useContext(AuthContext);
  const navigate = useNavigate();

  // 未登录跳转逻辑:副作用放在useEffect中执行
  useEffect(() => {
    if (!user) {
      navigate('/');
    }
  }, [user, navigate]);

  // 仅当user存在时触发请求,用可选链避免URL拼接报错
  const { data, loading } = useFetch(`/contracts/${user?.contractType || ''}`, {
    enabled: !!user
  });

  // 未登录时返回空或加载状态,避免渲染后续内容
  if (!user) {
    return null;
  }

  return (
    <div className="user-info">
      {loading ? <p>加载中...</p> : <p>合同类型:{data?.name}</p>}
    </div>
  );
};

同时修改useFetch实现,支持enabled参数:

import { useState, useEffect } from 'react';

function useFetch(url, options = {}) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    // 未启用或URL无效时,终止请求
    if (!options.enabled || !url.trim()) return;

    const fetchContract = async () => {
      setLoading(true);
      try {
        const res = await fetch(url);
        const result = await res.json();
        setData(result);
      } catch (err) {
        console.error('请求合同数据失败:', err);
      } finally {
        setLoading(false);
      }
    };

    fetchContract();
  }, [url, options.enabled]);

  return { data, loading };
}

export default useFetch;

方案二:拆分组件分离权限判断与业务逻辑

把需要user数据的业务逻辑抽成子组件,父组件先做权限判断和跳转,仅当user存在时才渲染子组件:

import { useContext, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import AuthContext from './AuthContext';
import UserInfoContent from './UserInfoContent';

const UserInfoPage = () => {
  const user = useContext(AuthContext);
  const navigate = useNavigate();

  useEffect(() => {
    if (!user) {
      navigate('/');
    }
  }, [user, navigate]);

  // 未登录时返回空,不渲染子组件
  if (!user) {
    return null;
  }

  // 子组件可安全使用user数据,内部调用useFetch
  return <UserInfoContent user={user} />;
};

子组件UserInfoContent实现:

import useFetch from './useFetch';

const UserInfoContent = ({ user }) => {
  // 此处user已确保存在,可直接读取contractType
  const { data, loading } = useFetch(`/contracts/${user.contractType}`);

  return (
    <div className="user-info">
      {loading ? <p>加载中...</p> : <p>合同类型:{data?.name}</p>}
    </div>
  );
};

export default UserInfoContent;

方案说明

两种方案均严格遵守React Hooks调用规则(所有Hooks在组件顶层调用,无条件包裹):

  • 方案一适合不想拆分组件的场景,通过控制useFetch触发时机避免报错;
  • 方案二更符合组件职责分离的最佳实践,权限判断与业务逻辑解耦,代码更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:48:22