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

React自定义Hook调用报错「渲染的钩子比上一次多」问题求解

错误原因排查

你遇到的Error: Rendered more hooks than during the previous render报错,直接原因是你在按钮的onClick事件回调中调用了自定义Hook useRandomJoke,完全违反了React Hook的核心使用规则。


React Hook 核心使用规则

React官方明确规定了Hook的两条不可违反的使用规则:

  • 只能在React函数组件的最顶层调用Hook,不可以在循环、条件判断、嵌套函数(比如事件回调、普通异步函数)中调用
  • 只能在React函数组件、自定义Hook内部调用Hook,不可以在普通JavaScript函数中调用

规则背后的逻辑:React是严格按照Hook在每次渲染中的调用顺序,来对应维护每个Hook关联的状态的。如果某一次渲染时,Hook的调用数量、顺序和上一次渲染不一致,React就无法正确匹配状态,就会抛出你遇到的这个错误。你当前的写法只有点击按钮时才会触发useRandomJoke调用,前后两次渲染的Hook数量必然不一致,自然触发报错。


现有代码的问题拆解

除了Hook调用位置错误外,你的代码还有一个设计问题:
当前useRandomJoke的useEffect依赖项是空数组,只会在Hook挂载时请求一次笑话,本身就不支持点击刷新的需求。


修复方案

方案1:调整自定义Hook,暴露主动刷新方法

修改useRandomJoke代码

把请求逻辑抽离成可复用函数,和笑话内容一起返回给调用方:

import { useEffect, useState } from "react";

const useRandomJoke = () => {
  const [joke, setJoke] = useState();
  // 抽离请求逻辑
  const fetchJoke = async () => {
    const res = await fetch("https://api.icndb.com/jokes/random");
    const data = await res.json();
    setJoke(data.value.joke);
  };

  // 组件挂载时默认请求一次
  useEffect(() => {
    fetchJoke();
  }, []);

  // 同时返回笑话内容和刷新方法
  return [joke, fetchJoke];
};

export default useRandomJoke;

修改App组件代码

在组件顶层调用自定义Hook,点击时直接调用返回的刷新方法即可,不需要额外维护jokes状态:

import useRandomJoke from './useRandomJoke';

function App() { 
  // 组件顶层调用Hook,符合调用规则
  const [jokes, fetchJoke] = useRandomJoke();
  return (
    <div className="App">
      <h1>随机笑话</h1>
      <p>{jokes}</p>
      {/* 点击直接触发刷新方法 */}
      <button onClick={fetchJoke}>
        点击获取新笑话
      </button>
    </div>
  ); 
} 
export default App;

方案2:给自定义Hook添加触发依赖项

如果你不想对外暴露请求方法,也可以通过外部依赖变化触发重新请求,同样符合Hook调用规则:

修改useRandomJoke代码

import { useEffect, useState } from "react";

// 接收外部传入的依赖项,依赖变化时自动重新请求
const useRandomJoke = (deps = []) => {
  const [joke, setJoke] = useState();
  useEffect(() => {
    const fetchJoke = async () => {
      const res = await fetch("https://api.icndb.com/jokes/random");
      const data = await res.json();
      setJoke(data.value.joke);
    };
    fetchJoke();
  }, deps);
  return joke;
};

export default useRandomJoke;

修改App组件代码

import { useState } from "react";
import useRandomJoke from './useRandomJoke';

function App() { 
  // 用一个标志位控制刷新
  const [refreshFlag, setRefreshFlag] = useState(0);
  // 顶层调用Hook,把标志位作为依赖传入
  const jokes = useRandomJoke([refreshFlag]);
  return (
    <div className="App">
      <h1>随机笑话</h1>
      <p>{jokes}</p>
      {/* 点击修改标志位,触发Hook重新请求 */}
      <button onClick={() => setRefreshFlag(prev => prev + 1)}>
        点击获取新笑话
      </button>
    </div>
  ); 
} 
export default App;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 01:36:04