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

Next.js 14 App Router报Hook规则错误但未违规,求解决方案

解决Next.js 14 App Router中无效Hook调用报错

问题背景

在Next.js 14 App Router中使用Redux开发,组件代码未违反Hooks规则,但收到无效Hook调用报错。已尝试调整Hooks位置、将pathname作为props传入,问题仍未解决。

报错详情

警告:无效的Hook调用。Hooks只能在函数组件的内部调用。可能由以下原因导致:

  1. React与渲染器(如React DOM)版本不匹配
  2. 违反了Hooks规则
  3. 应用中存在多个React副本
    ⨯ TypeError: Cannot read properties of null (reading 'useState')
    在SelectedItemsUser (./components/Header/SelectedItemUser.jsx:24:86)

组件代码

'use client';
import { FaShoppingCart } from 'react-icons/fa';
import { useState } from 'react';
import { FaChevronDown, FaChevronUp } from 'react-icons/fa';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import SkinCard from '@components/Skins/SkinCard';
import { SUPPORTED_CURRENCIES, paths } from '@app/paths';
import { usePathname } from 'next/navigation';

const SelectedItemsUser = (props) => {
    const { left, authenticated } = props;
    const [collapsed, setCollapsed] = useState(false);
    const { t } = useTranslation('trade');
    const selectedItems = useSelector(state => state.userInventory);
    const path = usePathname();

    const getItemPrice = (item) => {
        switch(path){
        case paths.BUY:
            return item.buyPrice;
        case paths.SELL:
            return item.price;
        case paths.TRADE:
            return item.tradePrice;
        default:
            return item.price;
        }
    };
    
    return (
        <div className={`w-full flex flex-col gap-1 rounded-6 overflow-hidden transition-all duration-200 select-none bg-bg-16 ${collapsed ? 'hover:bg-bg-29' : ''}`}>
            <div className={`w-full h-12 flex ${left ? 'flex-row-reverse' : 'flex-row'} items-center justify-between cursor-pointer ${collapsed ? 'bg-inherit' : 'transition-all duration-200 bg-bg-16 hover:bg-bg-29'} px-6 py-2`} onClick={() => setCollapsed(prev => !prev)}>
                <div className={`h-full flex ${left ? 'flex-row-reverse' : 'flex-row'} gap-2 items-center h-12 text-main`}>
                    <FaShoppingCart className="text-current text-12 xl:text-14" color="#FF4B4B"/>
                    
                    <p className="text-12 text-main font-medium flex flex-row items-center">
                        <span>{SUPPORTED_CURRENCIES[props.activeCurrency]?.symbol || '$'}{selectedItems.reduce((acc, item) => acc + (getItemPrice(item) / 100), 0).toFixed(2)}</span>
                    </p>
                    
                    {selectedItems && selectedItems.length >0 && (
                        <p className="text-10 bg-red text-main font-medium flex flex-row items-center py-0 px-1.5 rounded-full">{selectedItems.length}</p>
                    )}
                </div>
                <div className={`center gap-1.5 select-none text-main flex ${left ? 'flex-row' : 'flex-row-reverse'}`}>
                    {collapsed
                        ? (<FaChevronDown className={`text-current text-10 xl:text-12 ${left ? 'mr-2' : 'ml-2'}`} />)
                        : (<FaChevronUp className={`text-current text-10 xl:text-12 ${left ? 'mr-2' : 'ml-2'}`} />)
                    }
                    <p className="text-12 text-main font-medium capitalize">{t('trade_title')} {left ? t('trade_title_1') : t('trade_title_2')}</p>
                    <svg width="13" height="9" viewBox="0 0 13 9" fill="none" xmlns="http://www.w3.org/2000/svg">
                        <path d="M10.2055 5.67345H5.78483C5.45973 5.67345 5.19629 5.9369 5.19629 6.26199C5.19629 6.58696 5.45974 6.85053 5.78483 6.85053H10.3847L9.28262 8.00511C9.05819 8.24035 9.06689 8.6129 9.30213 8.83721C9.53725 9.06164 9.90982 9.05294 10.1342 8.81782L12.0959 6.76217C12.313 6.53476 12.313 6.17688 12.0959 5.94945L10.1342 3.89379C9.9098 3.65868 9.53723 3.64998 9.30213 3.87441C9.06689 4.09884 9.05819 4.47128 9.28262 4.7065L10.2055 5.67345Z" fill="#FF4B4B" />
                        <path d="M2.05325 1.96182H6.47397C6.79894 1.96182 7.06251 2.22527 7.06251 2.55036C7.06251 2.87545 6.79893 3.1389 6.47397 3.1389H1.87414L2.97617 4.29361C3.20048 4.52873 3.19178 4.9013 2.95666 5.12571C2.72155 5.35011 2.34897 5.34131 2.12457 5.1062L0.16273 3.05054C-0.0542433 2.82313 -0.0542433 2.46535 0.16273 2.23795L2.12457 0.182294C2.349 -0.0529462 2.72157 -0.0616471 2.95666 0.162784C3.19178 0.387214 3.20048 0.759785 2.97617 0.994882L2.05325 1.96182Z" fill="#FF4B4B" fillOpacity="0.25" />
                    </svg>
                </div>
            </div>
        </div>
    );
};

export default SelectedItemsUser;

解决方案

  • 排查多React副本:执行npm ls react或yarn list react查看依赖树,若存在多个版本,删除node_modules和锁文件(package-lock.json/yarn.lock)后重新安装;也可在package.json中强制指定统一的React版本。
  • 确保客户端组件标识正确:'use client';必须是文件的第一行,不能有任何注释、空行或代码在它之前,Next.js依赖该标识区分客户端/服务端组件,服务端组件内无法调用Hooks。
  • 检查Redux Provider配置:在App Router中,Redux Provider需放在客户端组件内(比如layout.tsx顶部添加'use client';,再包裹Provider),确保所有使用useSelector的组件都在Provider范围内。
  • 验证React与React DOM版本匹配:查看package.json中react和react-dom版本是否一致(如均为18.x.x),不一致则调整为相同版本后重新安装依赖。
  • 确认组件调用方式:确保SelectedItemsUser是以组件形式调用(<SelectedItemsUser />),而非普通函数调用(SelectedItemsUser()),后者会触发Hook调用错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 02:29:57