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

React自定义Hook无法在函数内使用报错问题求助

问题原因分析

你在getTheData点击事件回调中调用useBored和useTranslate,直接违反了React Hook的核心规则:Hook必须在组件的顶层作用域调用,不能嵌套在事件处理函数、条件语句或循环中。React依赖Hook的调用顺序维护状态,嵌套调用会破坏这个顺序,导致状态管理混乱,从而触发违规报错。

解决方案

以下提供两种可行方案,根据你的需求选择:

方案1:改用普通异步函数(推荐,适配手动触发场景)

因为你的需求是点击卡片才发起请求,而非组件渲染时自动请求,用普通异步函数比Hook更贴合场景:

  1. 重构请求逻辑为普通函数

    • 修改useBored.ts为fetchBored.ts:
      export const fetchBoredActivity = async (type: string): Promise<string[]> => {
        const res = await fetch(`http://www.boredapi.com/api/activity?type=${type}`);
        const data = await res.json();
        return [data.activity, data.link];
      };
      
    • 修改useTranslate.ts为translateText.ts:
      export const translateText = async (text: string): Promise<string> => {
        const res = await fetch(`https://api.mymemory.translated.net/get?q=${text}&langpair=en|ar`);
        const data = await res.json();
        return data.matches[0].translation;
      };
      
  2. 更新CardComp组件逻辑

    import React from "react";
    import { fetchBoredActivity } from "../hooks/fetchBored";
    import { translateText } from "../hooks/translateText";
    
    interface ICard {
      placeholder: string;
      image: string;
      type: string;
      setD: React.Dispatch<React.SetStateAction<string>>; 
    }
    
    const CardComp: React.FC<ICard> = ({ placeholder, image, type, setD }) => {
      const getTheData = async () => {
        try {
          // 先获取活动数据
          const [activity] = await fetchBoredActivity(type);
          // 再翻译内容
          const translatedText = await translateText(activity);
          // 更新父组件状态
          setD(translatedText);
        } catch (error) {
          console.error("请求出错:", error);
        }
      };
    
      return (
        <div>
          <div
            onClick={getTheData}
            className="flex flex-col justify-center items-center p-7 cursor-pointer font-mono bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-blue-700 via-blue-800 to-gray-900"
          >
            <div className=" ">
              <img src={image} className="w-[2.3em]" />
            </div>
            <h3>{placeholder}</h3>
          </div>
        </div>
      );
    };
    
    export default CardComp;
    

方案2:调整自定义Hook支持手动触发(保留Hook形式)

如果需要保留Hook的状态管理能力,可以给Hook添加触发控制,仅在手动触发时发起请求:

  1. 修改自定义Hook

    • 更新useBored.ts:
      import { useState, useEffect } from "react";
      
      export const useBored = (type: string, trigger: boolean) => {
        const [data, setData] = useState<string[]>([]);
        const [isLoading, setIsLoading] = useState<boolean>(false);
        const [error, setError] = useState<string>("");
      
        useEffect(() => {
          if (!trigger) return;
          const fetchData = async () => {
            setIsLoading(true);
            try {
              const res = await fetch(`http://www.boredapi.com/api/activity?type=${type}`);
              const result = await res.json();
              setData([result.activity, result.link]);
            } catch (e) {
              setError((e as Error).message);
            } finally {
              setIsLoading(false);
            }
          };
          fetchData();
        }, [type, trigger]);
      
        return { data, isLoading, error };
      };
      
    • 更新useTranslate.ts:
      import { useState, useEffect } from "react";
      
      export const useTranslate = (text: string, trigger: boolean) => {
        const [data, setData] = useState<null | string>(null);
        const [isLoading, setIsLoading] = useState<boolean>(false);
        const [error, setError] = useState<string>("");
      
        useEffect(() => {
          if (!trigger || !text) return;
          const fetchTranslation = async () => {
            setIsLoading(true);
            try {
              const res = await fetch(`https://api.mymemory.translated.net/get?q=${text}&langpair=en|ar`);
              const result = await res.json();
              setData(result.matches[0].translation);
            } catch (e) {
              setError((e as Error).message);
            } finally {
              setIsLoading(false);
            }
          };
          fetchTranslation();
        }, [text, trigger]);
      
        return { data, isLoading, error };
      };
      
  2. 更新CardComp组件

    import React, { useState, useEffect } from "react";
    import { useBored } from "../hooks/useBored";
    import { useTranslate } from "../hooks/useTranslate";
    
    interface ICard {
      placeholder: string;
      image: string;
      type: string;
      setD: React.Dispatch<React.SetStateAction<string>>; 
    }
    
    const CardComp: React.FC<ICard> = ({ placeholder, image, type, setD }) => {
      const [triggerFetch, setTriggerFetch] = useState(false);
      const { data: boredData } = useBored(type, triggerFetch);
      const { data: translatedData, isLoading: isTranslateLoading } = useTranslate(boredData[0] || "", triggerFetch);
    
      // 翻译完成后更新父组件状态并重置触发信号
      useEffect(() => {
        if (!isTranslateLoading && translatedData) {
          setD(translatedData);
          setTriggerFetch(false);
        }
      }, [isTranslateLoading, translatedData, setD]);
    
      const getTheData = () => {
        setTriggerFetch(true);
      };
    
      return (
        <div>
          <div
            onClick={getTheData}
            className="flex flex-col justify-center items-center p-7 cursor-pointer font-mono bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-blue-700 via-blue-800 to-gray-900"
          >
            <div className=" ">
              <img src={image} className="w-[2.3em]" />
            </div>
            <h3>{placeholder}</h3>
          </div>
        </div>
      );
    };
    
    export default CardComp;
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 12:45:43