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

React点击查看更多报Rendered more hooks than previous render错误咨询

这个报错的核心原因是违反了React Hooks的调用规则:所有Hook必须在组件的最顶层调用,不能在条件判断、循环、或者提前return的代码之后调用。你遇到的具体问题和调整方案如下:


具体问题点

1. 子组件Hook调用顺序不稳定(直接报错原因)

你的SecondCall和FirstCall组件都存在相同的逻辑问题:第二个useQuery('foo3')的调用放在了第一个useQuery的加载/错误判断return之后:

// 第一个useQuery调用
const {data, loading, error} = useQuery('foo2', ...)
if (!data || loading || error) return null // 这里提前返回,会导致后续的Hook不被调用
// 第二个useQuery调用,放在了可能提前返回的逻辑之后,不符合Hook规则
const {data: dataFLS, ...} = useQuery('foo3', ...)

首次渲染时第一个useQuery处于加载状态,组件直接return null,第二个useQuery没有执行;等到第一个请求返回后重新渲染,第二个useQuery才被调用,两次渲染的Hook数量不一致,就触发了报错。报错栈中出现的useMemo是useQuery内部调用的,本质还是Hook调用数量不稳定导致的。

2. 其他语法/规范问题

  • 组件参数解构错误:函数组件的第一个入参是整个props对象,你现在直接写const SecondCall = (properties) 拿到的是props对象本身,不是properties属性,正确写法是const SecondCall = ({properties}),同理FirstCall也有这个问题。
  • 重复声明变量:SecondCall和FirstCall里都先从useQuery解构了data,后面又写了const data = dataFLS?.foo3?.prods || [] 重复声明同一个变量会报错。
  • FirstCall组件名首字母小写:React要求自定义组件首字母必须大写,小写会被识别为原生HTML标签,导致渲染异常。
  • 不必要的数组嵌套:父组件传参时写properties={[properties]}、properties={[firstvalues]} 额外包了一层数组,完全没有必要,直接传原值即可。
  • 冗余的重复组件:FirstCall和SecondCall逻辑完全一致,只是传入的properties不同,不需要拆分两个组件,合并成一个即可减少维护成本。

修正方案

1. 合并通用子组件,调整Hook调用顺序

把所有Hook移到组件最顶层,避免提前返回导致Hook调用数量变化:

// 合并后的通用组件,首字母大写
export const ProductList = ({ properties }) => {
  // 所有Hook都放在组件最顶层
  const { data: foo2Data, loading: foo2Loading, error: foo2Error } = useQuery(
    'foo2',
    { variables: { value: properties } }
  );
  const list = foo2Data?.foo2?.prods || [];
  const items = list.map((prods) => prods.id);
  // 第二个useQuery也放在顶层,加skip参数,items为空时不发起请求
  const { data: foo3Data, loading: foo3Loading, error: foo3Error } = useQuery(
    'foo3',
    { variables: { items }, skip: !items.length }
  );

  // 所有Hook调用完成后再做返回判断
  if (foo2Loading || foo2Error || foo3Loading || foo3Error) return null;
  const renderList = foo3Data?.foo3?.prods || [];
  return renderList.map((products, index) => (
    <div key={`prods-${index}`}>
      <Row key={`related-products-${index}`}>
        "some divs get rendered with data" 
      </Row>
    </div>
  ));
};

2. 修正父组件逻辑

const SomeItems = ({guid}) => { // 修正props解构
  const {
    data: dataR,
    loading: loadingR,
    error: errorR
  } = useQuery(
    'foo',
    { variables: { guid } }
  );

  const list = dataR?.foo?.relatedProducts || [];
  const urls = list.map((prod) => prod.url);
  const properties = urls.map((url) => url.substr(url.lastIndexOf('/') + 1));
  const firstvalues = properties.slice(0, 3);

  const [showAll, setShowAll] = useState(false);
  return (
    <div>
      <h4>Shop this Project</h4>
      <div id="values">
        {/* 直接传对应的值,不需要包数组 */}
        <ProductList properties={showAll ? properties : firstvalues} />
        <div>
          <button
            id="products"
            type="button"
            onClick={() => setShowAll(true)}
            disabled={showAll} // 已经展开后可以禁用按钮
          >
            See More
          </button>
        </div>
      </div>
    </div>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 00:06:03