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

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
        }
    )
}

问题原因

  1. 混淆普通函数与自定义钩子:你的fetchUserInfo用到了React状态钩子,本质是自定义钩子,但没遵循钩子命名规则(必须以use开头),也没按钩子方式使用。
  2. 钩子调用方式错误:直接在组件内调用函数却不接收返回状态,且钩子必须在组件顶层调用,不能嵌套在逻辑里。
  3. useEffect依赖项缺失:依赖数组为空,但内部用到channelId等变量,会导致闭包问题,数据无法随channelLink更新。
  4. 返回值语法错误:返回对象时额外加了括号,被当作代码块导致返回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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:07:03