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

React组件报错:Rendered more hooks than during previous render 修复

修复React Hooks调用错误:Rendered more hooks than during the previous render

问题场景

开发FeaturedPool组件时,通过API获取APY最高的vault,找到目标池后尝试调用useVault获取用户持仓详情,触发了"Rendered more hooks than during the previous render"错误,错误出现在if (poolDetails?.id)条件判断内部调用useVault的代码段。

错误原因

React Hooks有严格的调用规则:所有Hook必须在组件的顶层作用域调用,不能嵌套在条件判断、循环、函数内部。React依赖Hook的调用顺序来维护状态关联,条件调用会导致每次渲染时Hook的数量或顺序不一致,从而触发该错误。

修复方案

方案1:将Hook移至组件顶层,依赖动态参数

把useVault移到组件顶层,传入poolDetails?.id作为参数,利用Hook的依赖更新机制,当poolDetails变化时自动重新获取数据。如果useVault不支持传入undefined,可通过默认值处理空参数场景。

方案2:封装子组件分离Hook逻辑

创建一个子组件,仅当poolDetails存在时才渲染,在子组件内部调用useVault,确保子组件的Hook始终在顶层调用,符合规则。

以下是方案1的具体实现:

完整修复代码

export const FeaturedPool = () => {
  // @ts-ignore
  const [loading, setLoading] = useState(true);
  const [APY, setAPY] = useState('');
  const [Liquidity, setLiquidity] = useState('');
  const [userPos, setUserPos] = useState('----');

  const [tvlId, setTvlId] = useState<String | undefined>('----');
  const [poolDetails, setPoolDetails] = useState<PoolInfo | undefined>();
  
  // 修复:将useVault移至组件顶层,传入poolDetails?.id(空值用默认字符串兜底)
  const { userPosition } = useVault(poolDetails?.id || '');
  
  // 监听userPosition变化,更新本地状态
  useEffect(() => {
    if (userPosition) {
      setUserPos(userPosition);
      console.log(userPosition);
    }
  }, [userPosition]);

  useEffect(() => {
    // 修复:将poolsArray移至useEffect内部,避免闭包问题
    const poolsArray: Record<string, string> = {};
    let counter = 0;
    const filteredPools = POOLS?.filter((x) => x.stableCoins) || [];
    
    filteredPools.map((pool) => {
      if (!pool.targetedAPYId) return;
      poolsArray[pool.targetedAPYId] = '';
      fetch('https://yields.llama.fi/chart/' + pool.targetedAPYId)
        .then((response) => response.json())
        .then((res) => {
          let result = res.data.at(-1).apyReward.toFixed(2);
          poolsArray[pool.targetedAPYId] = result;
          counter++;
          // 修复:用filteredPools.length替代硬编码的3,适配不同数量的池
          if (counter === filteredPools.length) {
            console.log('ALL FETCHED');
            const arr = Object.values(poolsArray);
            const max = Math.max(...arr.map(Number));
            const poolKey = Object.keys(poolsArray).find((key) => Number(poolsArray[key]) === max);
            
            if (poolKey) {
              const targetPool = POOLS.find((x) => x.targetedAPYId === poolKey);
              setPoolDetails(targetPool);
              
              if (targetPool?.tvlId) {
                setTvlId(targetPool.tvlId);
                fetchLiquidityData(targetPool.tvlId);
              }
            }
            
            setAPY(String(max));
            setLoading(false);
          }
        })
        .catch(err => {
          console.error('Fetch APY failed:', err);
          counter++;
          if (counter === filteredPools.length) {
            setLoading(false);
          }
        });
    });
  }, []);

  const wallet = useWallet();
  const app = useApp();

  const fetchLiquidityData = (liqId: string) => {
    fetch('https://yields.llama.fi/chart/' + liqId)
      .then((response) => response.json())
      .then((res) => {
        let liq = (Math.abs(Number(res.data.at(-1).tvlUsd)) / 1.0e6).toFixed(2) + 'M';
        setLiquidity(liq);
      })
      .catch(err => console.error('Fetch liquidity failed:', err));
  };

  const handleVaultIcon = (title: string) => {
    return title.toLowerCase() === 'quick vault';
  };

  return (
    <>
      {!loading ? (<p>Loaded</p>) : <p>Not Loaded</p>}
    </>
  );
};

额外优化点

  • 将poolsArray移至useEffect内部,避免组件渲染时重复创建及闭包问题
  • 用filteredPools.length替代硬编码的3,适配不同数量的稳定币池
  • 增加fetch请求的错误捕获,避免请求失败时页面一直处于加载状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 17:18:07