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"指令的文件属于客户端模块:
- 服务端组件运行在服务器环境,无法访问React客户端专属API(如
useState、useEffect),而自定义Hook依赖这些API - 直接在服务端组件中导入客户端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调用封装为独立客户端组件,再在服务端组件中引入:
- 创建
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> ) }
- 修改
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
相关产品推荐
相关产品推荐

