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

React商品页加载有效ID时误显404问题及解决方案咨询

问题根源

你遇到的核心问题是:组件初始状态下item为undefined,此时直接返回<NotFound />,但数据库查询是异步操作,请求完成前item一直是undefined,所以会先短暂显示404页面;等请求成功拿到数据后,item被赋值,才会渲染正常商品页。你之前加的延迟代码没有作用,因为wait(500)没有用await,不会阻塞代码执行,而且useEffect的依赖设置错误,导致逻辑重复触发。

解决方案

通过添加加载状态和明确的未找到状态,区分「正在加载」和「确实不存在」两种场景,同时修复异步请求的状态管理和useEffect依赖:

步骤1:添加状态变量

新增两个状态,分别标记加载状态和商品是否真的不存在:

const [isLoading, setIsLoading] = useState(true);
const [isNotFound, setIsNotFound] = useState(false);

步骤2:修复getItem异步函数

修改请求逻辑,在请求开始时标记加载中,请求结束后(成功/失败)取消加载状态;当请求返回404时,标记商品不存在:

const getItem = async () => {
    setIsLoading(true);
    setIsNotFound(false);
    try {
        const res = await itemService.getItemById(parseInt(id));
        setItem(res.data.product);
    } catch (error) {
        console.error(error);
        // 根据实际错误类型判断是否为404,比如检查error.response.status === 404
        setIsNotFound(true);
    } finally {
        setIsLoading(false);
    }
};

步骤3:调整useEffect依赖和逻辑

将useEffect的依赖改为id(只有商品ID变化时才重新请求),移除无用的延迟代码:

useEffect(() => {
    getItem();
    setImages(imageMap[parseInt(id)] || []); // 加默认值避免空数组报错
    getUserFromStorage();
}, [id]); // 依赖改为id,而非images

步骤4:调整渲染逻辑

根据加载状态和未找到状态,分别渲染对应内容:

// 加载中时显示加载组件(可自定义加载UI)
if (isLoading) {
    return <div>Loading...</div>;
}

// 确实不存在时才返回404
if (isNotFound || !item) {
    return <NotFound />;
}

// 否则渲染商品页面
return (
    // 原商品页面JSX...
);

修改后的完整代码

const ItemPage: React.FC = () => {
    const [item, setItem] = useState<Item>();
    const [quantity, setQuantity] = useState<number>(1);
    const [user, setUser] = useState<User>();
    const id = window.location.pathname.split("/")[2];
    const [images, setImages] = useState<string[]>([]);
    const [imageIndex, setImageIndex] = useState<number>(0);
    const [size, setSize] = useState<string>("Small");
    const [authenticationMessage, setAuthenticationMessage] = useState<string>("");
    const [popupMessage, setPopupMessage] = useState<string>("");
    // 新增状态
    const [isLoading, setIsLoading] = useState(true);
    const [isNotFound, setIsNotFound] = useState(false);
    
    const nav = useNavigate();
    const imageMap: { [key: number]: string[] } = {
        1: nascarImages,
        2: heartlessImages,
        3: anotherImages
    };
    const cookies = new Cookies();
    const jwt = cookies.get("jwttoken");

    useEffect(() => {
        getItem();
        setImages(imageMap[parseInt(id)] || []);
        getUserFromStorage();
    }, [id]); // 依赖改为id

    const getItem = async () => {
        setIsLoading(true);
        setIsNotFound(false);
        try {
            const res = await itemService.getItemById(parseInt(id));
            setItem(res.data.product);
        } catch (error) {
            console.error(error);
            // 根据实际错误类型判断是否为404
            if (error instanceof Error && /* 补充你的404判断逻辑,比如error.response.status === 404 */) {
                setIsNotFound(true);
            }
        } finally {
            setIsLoading(false);
        }
    };

    const getUserFromStorage = async () => {
        const userId = sessionStorage.getItem("id");
        if (userId !== null) {
            try {
                const res = await userService.getUserById(parseInt(userId), jwt);
                setUser(res.data.user);
            } catch (error) {
                console.error(error);
            }
        }
    };

    const nextImage = () => {
        setImageIndex(prev => prev === images.length - 1 ? 0 : prev + 1);
    };

    const previousImage = () => {
        setImageIndex(prev => prev === 0 ? images.length - 1 : prev - 1);
    };

    const addToCart = async () => {
        if (!user) {
            setAuthenticationMessage("You need to be logged in to add items to cart");
            return;
        }
        if (!item) return;
        
        try {
            const res = await cartService.addToCart(user.id, item.id, quantity, size, jwt);
            if (res?.status === "success") {
                setPopupMessage("Item succesfully added to cart");
                setTimeout(() => setPopupMessage(""), 3000);
            } else if (res?.status === "unauthorized") {
                setPopupMessage("It seems that you are not authenticated anymore, please login again");
                setTimeout(() => setPopupMessage(""), 3000);
            }
        } catch (error) {
            console.error(error);
        }
    };

    if (isLoading) {
        return <div>Loading...</div>; // 可替换为自定义加载组件
    }

    if (isNotFound || !item) {
        return <NotFound />;
    }

    return (
        <div className="itemContainer">
          <div className="itemImages">
            <button className="leftArrow" onClick={previousImage}></button>
            <img className="itemPicture" src={images[imageIndex]} alt="logo" />
            <button className="rightArrow" onClick={nextImage}></button>
          </div>
          <div className="itemInfo">
            <div className="itemTitle">
              <h1 className="itemHeader">{item.name}</h1>
            </div>
            <div className={`popupMessage ${popupMessage.length === 0 ? '' : 'show'}`}>
                <p className="popupText">{popupMessage}</p>
            </div>
            <div>
              <p className="itemDescription">{item.description}</p>
            </div>
            <button className="toCartButton" onClick={addToCart}>
              Add to cart
            </button>
            {authenticationMessage && (
                <p className="authenticationMessage">{authenticationMessage}</p>
            )}
            <div className="sizeContainer">
                <button className="sizeButton" onClick={()=>setSize("Small")}>S</button>
                <button className="sizeButton" onClick={()=>setSize("Medium")}>M</button>
                <button className="sizeButton" onClick={()=>setSize("Large")}>L</button>
            </div> 
          </div>
        </div>
      );
}
export default ItemPage;

额外优化点

  • 给images设置默认空数组,避免imageIndex越界报错
  • 简化nextImage和previousImage的逻辑,使用函数式更新更可靠
  • 给getUserFromStorage添加错误处理,避免未捕获异常
  • 在addToCart里提前判断user和item,减少嵌套逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 08:30:08