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

React应用集成react-use-cart后触发Invalid Hook Error求助

React Invalid Hook Error 排查与修复(使用react-use-cart时)

问题场景

我正在使用React搭建模拟电商网站,完成基础框架后引入react-use-cart包处理购物车逻辑,已用CartProvider包裹整个应用,但启动后页面无法加载,抛出Invalid Hook Error,即使将CartProvider移到index.js中,错误依然存在。

相关代码

App.js

import React, { useState } from "react";
import './css/App.css';
import {
  BrowserRouter,
  Routes,
  Route,
} from "react-router-dom";
import NavBar from "./components/NavBar"
import ProductsScreen from "./screens/ProductsScreen"
import ProductDetailScreen from "./screens/ProductDetailScreen"
import HomeScreen from "./screens/HomeScreen"
import LoginScreen from "./screens/LoginScreen"
import RegisterScreen from "./screens/RegisterScreen";
import CartScreen from "./screens/CartScreen"
import { CartProvider } from "react-use-cart";


const App = () => {
  return (
    <>
      <BrowserRouter>
        <CartProvider>
          <NavBar />
          <Routes>
            <Route path='/' element={<HomeScreen/>}/>
            <Route path='/products' element={<ProductsScreen/>}/>
            <Route path='/product/:id' element={<ProductDetailScreen/>}/>
            <Route path='/login' element={<LoginScreen/>}/>
            <Route path='/register' element={<RegisterScreen/>}/>
            <Route path='/cart' element={<CartScreen/>}/>
          </Routes>
        </CartProvider>
      </BrowserRouter>
    </>
  )
}

export default App;

ProductDetailScreen.js

import { useState, useEffect }from 'react'
import { Link, useParams } from 'react-router-dom'
import axios from 'axios'
import "../css/ProductDetailScreen.css"
import { useCart } from "react-use-cart";
// import Slideshow from '../components/SlideShow'

const { addItem, updateItemQuantity } = useCart();
const ProductDetailScreen = ( {match} ) => {

    const [product, setProduct] = useState({})
    const { id } = useParams()

    useEffect(() => {
        const getProduct = async () => {
          const { data } = await axios.get(`/api/products/${id}`)
          setProduct(data)
        }

        getProduct()
      }, [])

    return (
    <>
        <Link to ='/products'>Go Back</Link>
        <div className="wrapper">
            <div className="detail-grid">
                <div className='imgContainer'> 
                    <img className='cover_img1' src={product.cover_img} alt='Videogame Cover Art'/>
                    <div className='carousel'>
                        {product.images?.map((image, index) => (
                            <img key={index} src={image} alt="Videogame Gameplay"></img>
                        ))}
                    </div>
                    {/*Replace carousel div with actual image slideshow*/}
                </div>
                <div>
                    <div className='textContainer'>
                        <h1 className='title1'>{product.title}</h1>
                        <h3 className='descriptionHeader'>Description:</h3>
                        <p className='description1'>{product.description}</p>
                        <h3 className='developer1'>Developer: {product.developer}</h3>
                        <h3 className='publisher1'>Publisher: {product.publisher}</h3>
                        <h3 className='releaseDate1'>Release Date: {product.releaseDate}</h3>
                        <h3 className='genre1'>Genre: {product.genre}</h3>
                    </div>
                    <div className='checkOutContainer'>
                        <h3 className='price1'>Price: ${product.price}</h3>
                        <button onClick={() => updateItemQuantity(product.id, product.quantity - 1)}> - </button>
                        <span>{product.quantity}</span>
                        <button onClick={() => updateItemQuantity(product.id, product.quantity + 1)}> + </button>
                        <button onClick={() => addItem(product)} className='addCart' type='button'>Add to Cart</button>
                    </div>
                </div>
            </div>
        </div>
    </>
    )
}

export default ProductDetailScreen

错误截图

错误截图

错误原因

问题出在React钩子的调用位置违反规则:在ProductDetailScreen.js中,useCart()被写在了组件函数ProductDetailScreen的外部。React要求所有钩子必须在:

  • React函数组件的顶层作用域内调用
  • 自定义钩子的顶层作用域内调用

不能在组件外部、条件判断、循环或嵌套函数中调用钩子,否则会触发Invalid Hook Error。

修复方案

将useCart()的调用移到ProductDetailScreen组件函数内部的顶层位置:

修复后的ProductDetailScreen.js

import { useState, useEffect }from 'react'
import { Link, useParams } from 'react-router-dom'
import axios from 'axios'
import "../css/ProductDetailScreen.css"
import { useCart } from "react-use-cart";
// import Slideshow from '../components/SlideShow'

const ProductDetailScreen = ( {match} ) => {
    // 将useCart移到组件内部顶层
    const { addItem, updateItemQuantity } = useCart();
    
    const [product, setProduct] = useState({})
    const { id } = useParams()

    useEffect(() => {
        const getProduct = async () => {
          const { data } = await axios.get(`/api/products/${id}`)
          setProduct(data)
        }

        getProduct()
      }, [])

    return (
    <>
        <Link to ='/products'>Go Back</Link>
        <div className="wrapper">
            <div className="detail-grid">
                <div className='imgContainer'> 
                    <img className='cover_img1' src={product.cover_img} alt='Videogame Cover Art'/>
                    <div className='carousel'>
                        {product.images?.map((image, index) => (
                            <img key={index} src={image} alt="Videogame Gameplay"></img>
                        ))}
                    </div>
                    {/*Replace carousel div with actual image slideshow*/}
                </div>
                <div>
                    <div className='textContainer'>
                        <h1 className='title1'>{product.title}</h1>
                        <h3 className='descriptionHeader'>Description:</h3>
                        <p className='description1'>{product.description}</p>
                        <h3 className='developer1'>Developer: {product.developer}</h3>
                        <h3 className='publisher1'>Publisher: {product.publisher}</h3>
                        <h3 className='releaseDate1'>Release Date: {product.releaseDate}</h3>
                        <h3 className='genre1'>Genre: {product.genre}</h3>
                    </div>
                    <div className='checkOutContainer'>
                        <h3 className='price1'>Price: ${product.price}</h3>
                        <button onClick={() => updateItemQuantity(product.id, product.quantity - 1)}> - </button>
                        <span>{product.quantity}</span>
                        <button onClick={() => updateItemQuantity(product.id, product.quantity + 1)}> + </button>
                        <button onClick={() => addItem(product)} className='addCart' type='button'>Add to Cart</button>
                    </div>
                </div>
            </div>
        </div>
    </>
    )
}

export default ProductDetailScreen

额外注意事项

  • 确保所有使用useCart钩子的组件都在CartProvider的包裹范围内,你的App.js中已经正确包裹,这部分无需修改。
  • 检查其他组件是否存在类似的钩子调用位置错误,避免重复触发同类问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 17:25:19