NextJS导入含'use client'的外部数据获取函数报错问题
解决React自定义钩子导入报错:fetchUserInfo is not a function
问题场景
为了减少重复代码,我把用到useEffect、useState钩子的fetchUserInfo数据获取逻辑移到了外部文件,并且在文件顶部声明了"use client"。但导入后React抛出错误:
Error: (0 , fetchUserInfo__WEBPACK_IMPORTED_MODULE_2_.default) is not a function
原组件代码
import fetchUserInfo from "./fetchUserInfo" const VideoTestimonial = ({ channelLink }) => { fetchUserInfo(channelLink) return ( <div> <p>Hello world</p> </div> ) } export default VideoTestimonial
原函数代码
"use client" import { useEffect, useState } from "react" const API_KEY = "INSERT_KEY" export default function fetchUserInfo(channelLink) { const channelId = channelLink; const API_URL = `https://www.googleapis.com/youtube/v3/channels?part=statistics&id=${channelId}&key=${API_KEY}` const PIC_URL = `https://www.googleapis.com/youtube/v3/channels?part=snippet&id=${channelId}&fields=items%2Fsnippet%2Fthumbnails&key=${API_KEY}` const USERNAME_URL = `https://www.googleapis.com/youtube/v3/channels?part=brandingSettings&id=${channelId}&key=${API_KEY}` const [username, setUsername] = useState("loading") const [profileImage, setprofileImage] = useState("") const [subCount, setSubCount] = useState("loading") useEffect(() => { fetch(API_URL) .then((res) => res.json()) .then((data) => { setSubCount(Number(data.items[0].statistics.subscriberCount).toLocaleString("de-DE")) }) .catch((error) => { }); fetch(PIC_URL) .then((res) => res.json()) .then((data) => { setprofileImage(data.items[0].snippet.thumbnails.medium.url) }) .catch((error) => { }); fetch(USERNAME_URL) .then((res) => res.json()) .then((data) => { setUsername(data.items[0].brandingSettings.channel.title) }) .catch((error) => { }); }, []); return ( { username: username, profileImage: profileImage, subCount: subCount } ) }
问题原因
- 混淆普通函数与自定义钩子:你的
fetchUserInfo用到了React状态钩子,本质是自定义钩子,但没遵循钩子命名规则(必须以use开头),也没按钩子方式使用。 - 钩子调用方式错误:直接在组件内调用函数却不接收返回状态,且钩子必须在组件顶层调用,不能嵌套在逻辑里。
- useEffect依赖项缺失:依赖数组为空,但内部用到
channelId等变量,会导致闭包问题,数据无法随channelLink更新。 - 返回值语法错误:返回对象时额外加了括号,被当作代码块导致返回
undefined,最终让调用方认为不是函数。
修复方案
步骤1:将函数改为规范的自定义钩子
重命名为useUserInfo(必须以use开头),修复返回值语法,完善useEffect依赖项,优化请求逻辑。
修正后的钩子文件(useUserInfo.js)
"use client"; import { useEffect, useState } from "react"; const API_KEY = "INSERT_KEY"; export default function useUserInfo(channelLink) { const channelId = channelLink; const [username, setUsername] = useState("loading"); const [profileImage, setProfileImage] = useState(""); const [subCount, setSubCount] = useState("loading"); useEffect(() => { // 封装异步请求,用Promise.all并行提升性能 const fetchUserDetails = async () => { if (!channelId) return; try { const [statsRes, picRes, usernameRes] = await Promise.all([ fetch(`https://www.googleapis.com/youtube/v3/channels?part=statistics&id=${channelId}&key=${API_KEY}`), fetch(`https://www.googleapis.com/youtube/v3/channels?part=snippet&id=${channelId}&fields=items%2Fsnippet%2Fthumbnails&key=${API_KEY}`), fetch(`https://www.googleapis.com/youtube/v3/channels?part=brandingSettings&id=${channelId}&key=${API_KEY}`) ]); // 处理订阅数 const statsData = await statsRes.json(); setSubCount(Number(statsData.items[0].statistics.subscriberCount).toLocaleString("de-DE")); // 处理头像 const picData = await picRes.json(); setProfileImage(picData.items[0].snippet.thumbnails.medium.url); // 处理用户名 const usernameData = await usernameRes.json(); setUsername(usernameData.items[0].brandingSettings.channel.title); } catch (error) { // 错误处理,给用户反馈 setUsername("加载失败"); setSubCount("加载失败"); console.error("获取用户信息出错:", error); } }; fetchUserDetails(); }, [channelId]); // 添加channelId作为依赖,参数变化时重新请求 return { username, profileImage, subCount }; }
步骤2:在组件中正确使用自定义钩子
在组件顶层调用钩子,接收返回状态并渲染。
修正后的组件代码
import useUserInfo from "./useUserInfo"; const VideoTestimonial = ({ channelLink }) => { // 在组件顶层调用自定义钩子,获取状态 const { username, profileImage, subCount } = useUserInfo(channelLink); return ( <div> <p>用户名: {username}</p> {profileImage && <img src={profileImage} alt="用户头像" width="100" />} <p>订阅数: {subCount}</p> </div> ); }; export default VideoTestimonial;
关键说明
- 自定义钩子必须以
use开头,这是React强制约定,确保React能识别并应用钩子规则(比如不能在条件、循环中调用)。 - 使用
Promise.all并行请求多个接口,比依次调用fetch更高效,减少等待时间。 - 给
useEffect添加正确的依赖项,确保channelLink变化时能重新请求数据。 - 增加错误处理,避免接口请求失败导致页面无响应或显示异常。
内容的提问来源于stack exchange,提问作者nome cognome
相关产品推荐
相关产品推荐

