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

React Hooks报错:渲染Hook数量不符及调用位置错误求助

解决Antd React Table可编辑单元格的Hook错误与警告

问题现象

运行代码时出现两个问题:

  • 抛出错误:Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement in React Hooks
  • 出现警告:Warning: Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. You can only call Hooks at the top level of your React function

原代码

const EditableCell = (initialValue: any) => {
  const [value, setValue] = React.useState(initialValue);

  const onChange = (e: any) => {
    setValue(e.target.value);
  };

  React.useEffect(() => {
    setValue(initialValue);
  }, [initialValue]);

  return <Input type="text" value={value} onChange={onChange} />;
};


const ParameterTable: React.FC<ParameterTableProps> = () => {
  const {
    decisionMetadataInput: input,
    decisionMetadataOutput: output,
    paramList
  } = testData.data;
  const inMeta = React.useMemo(() => input?.metadata ?? [], [input]);
  const outMeta = React.useMemo(() => output?.metadata ?? [], [output]);
  const allColumn = React.useMemo(
    () =>
      [...inMeta, ...outMeta].map((meta) => (
        <Column
          title={
            <>
              <span>{meta.field}</span>
              <br />
              <Typography.Text italic>({meta.type})</Typography.Text>
            </>
          }
          key={meta.field}
          dataIndex={["data", meta.field]}
          render={(dataIndex) => EditableCell(dataIndex)}
        />
      )),
    [inMeta, outMeta]
  );
  const datasource = React.useMemo(
    () =>
      paramList.map((param) => {
        const inParam = param?.paramInput?.param ?? [];
        const outParam = param?.paramOutput?.param ?? [];
        const data = [...inParam, ...outParam].reduce(
          (prev, current) => ({
            ...prev,
            [current.field]: current.value
          }),
          {}
        );
        return { data, num: param.paramOrder };
      }),
    [paramList]
  );

  return (
    <Table dataSource={datasource} rowKey="num" pagination={false}>
      <Column title="F" dataIndex="num" />
      {allColumn}
    </Table>
  );
};

问题原因

  • EditableCell是包含React Hooks的组件,但你在Column的render回调里直接以普通函数的方式调用它,相当于在嵌套函数中执行Hook,违反了React的Hook规则:Hook必须在组件的顶层作用域调用,不能嵌套在回调、循环、条件或其他Hook内部。
  • 同时,useMemo包裹的allColumn生成逻辑中,每次执行render回调时调用EditableCell函数,会导致React无法稳定追踪Hook的调用顺序和数量,触发"渲染的Hook数量不一致"的错误。

解决代码

1. 修正EditableCell为标准React组件

// 定义为接收props的FC组件
const EditableCell: React.FC<{ initialValue: any }> = ({ initialValue }) => {
  const [value, setValue] = React.useState(initialValue);

  // 修正事件类型为React标准类型
  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setValue(e.target.value);
  };

  React.useEffect(() => {
    setValue(initialValue);
  }, [initialValue]);

  return <Input type="text" value={value} onChange={onChange} />;
};

2. 修正Column的render回调调用方式

在allColumn的render中,以组件标签形式使用EditableCell,而不是调用函数:

const allColumn = React.useMemo(
  () =>
    [...inMeta, ...outMeta].map((meta) => (
      <Column
        title={
          <>
            <span>{meta.field}</span>
            <br />
            <Typography.Text italic>({meta.type})</Typography.Text>
          </>
        }
        key={meta.field}
        dataIndex={["data", meta.field]}
        // 改为组件标签形式传递props
        render={(dataIndex) => <EditableCell initialValue={dataIndex} />}
      />
    )),
  [inMeta, outMeta]
);

完整修正后的ParameterTable

const EditableCell: React.FC<{ initialValue: any }> = ({ initialValue }) => {
  const [value, setValue] = React.useState(initialValue);

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setValue(e.target.value);
  };

  React.useEffect(() => {
    setValue(initialValue);
  }, [initialValue]);

  return <Input type="text" value={value} onChange={onChange} />;
};

const ParameterTable: React.FC<ParameterTableProps> = () => {
  const {
    decisionMetadataInput: input,
    decisionMetadataOutput: output,
    paramList
  } = testData.data;
  const inMeta = React.useMemo(() => input?.metadata ?? [], [input]);
  const outMeta = React.useMemo(() => output?.metadata ?? [], [output]);
  const allColumn = React.useMemo(
    () =>
      [...inMeta, ...outMeta].map((meta) => (
        <Column
          title={
            <>
              <span>{meta.field}</span>
              <br />
              <Typography.Text italic>({meta.type})</Typography.Text>
            </>
          }
          key={meta.field}
          dataIndex={["data", meta.field]}
          render={(dataIndex) => <EditableCell initialValue={dataIndex} />}
        />
      )),
    [inMeta, outMeta]
  );
  const datasource = React.useMemo(
    () =>
      paramList.map((param) => {
        const inParam = param?.paramInput?.param ?? [];
        const outParam = param?.paramOutput?.param ?? [];
        const data = [...inParam, ...outParam].reduce(
          (prev, current) => ({
            ...prev,
            [current.field]: current.value
          }),
          {}
        );
        return { data, num: param.paramOrder };
      }),
    [paramList]
  );

  return (
    <Table dataSource={datasource} rowKey="num" pagination={false}>
      <Column title="F" dataIndex="num" />
      {allColumn}
    </Table>
  );
};

关键注意点

  • React组件必须通过标签形式(<Component />)使用,不能像普通函数一样调用,这样React才能正确识别组件并追踪内部的Hook调用。
  • 严格遵守React Hook规则:所有Hook必须在组件的顶层作用域调用,禁止嵌套在任何回调、循环、条件判断或其他Hook内部,否则会导致状态管理异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 05:50:37