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

Next.js 13中useRouter为null、router.isReady失效问题求助

Next.js 13 useRouter 报错:Cannot read properties of null (reading 'useContext')

问题描述

我在使用Next.js 13时遇到了一个无法解决的错误,代码如下:

import { useRouter } from 'next/navigation';

async function getCheckoutInfo() {
    const router = useRouter();
    if (router.isReady) {
        console.log('Router query:', router.query);
    } else {
        console.log('Router is not ready yet')
    }
    
    return 'test';
}


export default async function CheckoutPage() {
    const info = await getCheckoutInfo();

    return(
        <div>
            <h1>Checkout </h1>
            <p>{info}</p>
        </div>
    )
}

报错信息如下:

Unhandled Runtime Error
Error: Cannot read properties of null (reading 'useContext')

Call Stack
Object.useContext
webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react/cjs/react.shared-subset.development.js (1428:31)
useRouter
webpack-internal:///(sc_server)/./node_modules/next/dist/client/components/navigation.js (91:32)

我觉得useRouter没有按预期工作。之前用useEffect()能正常运行,但我以为Next.js 13里不需要这么做了。


问题原因

useRouter 是React Hook,必须严格遵循Hook规则:

  • 只能在React组件的顶层调用,或者在自定义Hook的顶层调用
  • 不能在普通异步函数(比如你的getCheckoutInfo)里调用——这类函数不属于React组件或自定义Hook的执行上下文,无法获取React内部的Context,因此抛出useContext相关错误。

另外,你的getCheckoutInfo不是自定义Hook(自定义Hook必须以use开头命名),即便它是同步函数,也不能在组件内部的异步逻辑中调用。


解决办法

方法1:将逻辑改为自定义Hook(适合复用场景)

自定义Hook必须以use开头,且在组件顶层调用:

import { useRouter } from 'next/navigation';

// 自定义Hook,命名以use开头
function useCheckoutInfo() {
    const router = useRouter();
    if (router.isReady) {
        console.log('Router query:', router.query);
    } else {
        console.log('Router is not ready yet')
    }
    
    return 'test';
}

export default function CheckoutPage() {
    // 在组件顶层调用自定义Hook
    const info = useCheckoutInfo();

    return(
        <div>
            <h1>Checkout </h1>
            <p>{info}</p>
        </div>
    )
}

方法2:直接在组件顶层使用useRouter(适合简单场景)

如果不需要复用逻辑,直接把路由相关代码放在组件顶层:

import { useRouter } from 'next/navigation';

export default function CheckoutPage() {
    const router = useRouter();
    
    if (router.isReady) {
        console.log('Router query:', router.query);
    } else {
        console.log('Router is not ready yet')
    }

    return(
        <div>
            <h1>Checkout </h1>
            <p>test</p>
        </div>
    )
}

方法3:结合异步请求与路由信息

如果getCheckoutInfo需要基于路由参数做异步请求,可通过useEffect监听路由状态变化:

import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';

export default function CheckoutPage() {
    const router = useRouter();
    const [info, setInfo] = useState('Loading...');

    useEffect(() => {
        async function fetchCheckoutInfo() {
            if (router.isReady) {
                console.log('Router query:', router.query);
                // 根据路由参数发起异步请求
                const res = await fetch(`/api/checkout?orderId=${router.query.orderId}`);
                const data = await res.json();
                setInfo(data.info);
            }
        }
        fetchCheckoutInfo();
    }, [router.isReady, router.query]);

    return(
        <div>
            <h1>Checkout </h1>
            <p>{info}</p>
        </div>
    )
}

补充说明

Next.js 13 App Router中的useRouter确实无需像Pages Router那样必须依赖useEffect等待isReady,但前提是要在组件或自定义Hook的顶层正确调用它。普通异步函数不具备Hook的执行上下文,必然会报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 16:01:09