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

NextJS 14+自定义Hook报错:default is not a function求助

解决Next.js服务端组件调用客户端自定义Hook的报错:Error: (0 , _app_usePaginate__WEBPACK_IMPORTED_MODULE_2__.default) is not a function

问题背景

在Next.js App Router中构建分页组件时,使用带"use client"指令的自定义Hook usePaginate处理数据获取与分页导航,但直接在默认的服务端组件page.tsx中导入调用时,触发上述报错。

报错信息

Error: (0 , _app_usePaginate__WEBPACK_IMPORTED_MODULE_2__.default) is not a function

相关代码

usePaginate.ts(app目录下)

"use client"
import { useState, useEffect, useCallback } from 'react'

const usePaginate = () => {
    const [data, setData] = useState([])
    const [currentPage, setCurrentPage] = useState(1)

    const fetchData = useCallback(async () => {
        try {
            let response = await fetch(`https://give-me-users-forever.vercel.app/api/users/${currentPage}/next`)
            let jsonResponse = await response.json();

            setData(jsonResponse.users);
        } catch (error) {
            console.log(error)
        }
    }, [currentPage])

    const nextPage = () => setCurrentPage(prev => prev + 1)

    const prevPage = () => {
        if (currentPage > 1) { setCurrentPage(prev => prev - 1) }
    }

    useEffect(() => {
        fetchData()
    }, [currentPage, fetchData])

    return { data, currentPage, nextPage, prevPage }
}

export default usePaginate

page.tsx(app目录下)

import usePaginate from './usePaginate'

const Home = async () => {

  const { data, currentPage, nextPage, prevPage } = usePaginate()

  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">

      Data list:
      {data.map((item: any) => (<div key={item.id}>{item.FirstNameLastName}</div>))}
    </main>
  );
}

export default Home

问题原因

Next.js App Router中,page.tsx默认是服务端组件,而带有"use client"指令的文件属于客户端模块:

  1. 服务端组件运行在服务器环境,无法访问React客户端专属API(如useState、useEffect),而自定义Hook依赖这些API
  2. 直接在服务端组件中导入客户端Hook会导致Webpack模块解析异常,触发default is not a function错误

解决方案

方案1:将page.tsx转为客户端组件

在page.tsx顶部添加"use client"指令,使其成为客户端组件,即可正常调用客户端Hook:

"use client" // 添加该行指令
import usePaginate from './usePaginate'

// 移除async,客户端组件无需async声明(除非使用客户端异步逻辑)
const Home = () => {
  const { data, currentPage, nextPage, prevPage } = usePaginate()

  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <div className="mb-8">
        <button onClick={prevPage} disabled={currentPage === 1} className="mr-4">上一页</button>
        <span className="mr-4">当前页:{currentPage}</span>
        <button onClick={nextPage}>下一页</button>
      </div>
      <div>
        Data list:
        {data.map((item: any) => (<div key={item.id}>{item.FirstNameLastName}</div>))}
      </div>
    </main>
  );
}

export default Home

方案2:封装客户端组件,在服务端组件中引入

若需保持page.tsx为服务端组件,可将分页UI与Hook调用封装为独立客户端组件,再在服务端组件中引入:

  1. 创建app/PaginationList.tsx客户端组件:
"use client"
import usePaginate from './usePaginate'

export default function PaginationList() {
  const { data, currentPage, nextPage, prevPage } = usePaginate()

  return (
    <div>
      <div className="mb-8">
        <button onClick={prevPage} disabled={currentPage === 1} className="mr-4">上一页</button>
        <span className="mr-4">当前页:{currentPage}</span>
        <button onClick={nextPage}>下一页</button>
      </div>
      <div>
        Data list:
        {data.map((item: any) => (<div key={item.id}>{item.FirstNameLastName}</div>))}
      </div>
    </div>
  )
}
  1. 修改page.tsx(保持服务端组件属性):
import PaginationList from './PaginationList'

const Home = async () => {
  // 可在此添加服务端专属数据获取逻辑
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <PaginationList />
    </main>
  );
}

export default Home

关键注意事项

  • App Router中服务端组件与客户端组件边界清晰:服务端组件不能调用客户端Hook、不能访问window等客户端专属API
  • 带"use client"指令的模块仅能在客户端组件或其他客户端模块中使用
  • 服务端组件如需使用客户端交互逻辑,必须通过封装客户端组件的方式引入

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 21:13:29