React Rendered fewer hooks than expected报错排查
问题根因
你的判断完全正确,报错的核心诱因就是直接以函数调用形式执行组件,而非通过JSX语法渲染组件。
React的Hook顺序校验是和组件节点绑定的:
- 用
<Component />的JSX形式渲染时,React会将其识别为独立组件节点,组件内部的Hook调用会被归到该节点独立的Hook链表下,只要节点挂载/卸载顺序稳定就不会触发顺序类报错。 - 直接写
Component()调用函数时,React不会将其识别为独立组件,组件内部的所有Hook调用会被合并到当前父组件(也就是这里的PrepareText)的Hook链表中。
你的场景里,reactStringReplace匹配到的占位符数量会随text内容变化,条件分支也会选择渲染不同的Pill组件,只要两次渲染间调用的Pill函数数量不一致,PrepareText的Hook调用总数就会发生变化,直接触发「Rendered fewer hooks than expected」报错。
另外你传入的EntityPill/PlayerPill是每次渲染都重新创建的内联箭头函数,内部直接执行Cell组件,等于把Cell组件内的Hook(比如你引入的useSteamUser)全部插入到PrepareText的Hook调用队列里,Hook顺序完全无法稳定。
修复方案
核心原则:所有React组件必须通过JSX语法渲染,禁止直接以普通函数形式调用组件。
- 首先调整
PrepareText组件的类型定义和渲染逻辑,将接收的Pill参数改为组件类型而非普通函数,渲染时用JSX写法:
import reactStringReplace from 'react-string-replace'; import type { ComponentType } from 'react'; import { Entity, Player } from './LogItem'; // 定义Pill组件统一接收的Props类型,和你现有CellProps保持一致即可 type PillBaseProps = { value: string | null; restricted?: boolean; }; export default function PrepareText({ subjects, text, restricted, EntityPill, PlayerPill, }: { subjects: { player: Player; entity: Entity }; text: string; restricted?: boolean; EntityPill: ComponentType<PillBaseProps>; PlayerPill: ComponentType<PillBaseProps>; }) { return ( <> {reactStringReplace(text, /(\${\w+})/g, (match, i) => { const key = match .replace('${', '') .replace('}', '') as keyof typeof subjects; const CurrentPill = key === 'player' ? PlayerPill : EntityPill; return ( <span key={i}> {/* 用JSX语法渲染组件,Hook会被绑定到独立节点 */} <CurrentPill value={subjects[key]} restricted={restricted} /> </span> ); })} </> ); }
- 调整PrepareText的调用代码,直接传入配置中存储的组件引用,不要包内联箭头函数做函数调用:
<PrepareText subjects={{ player: data.player, entity: data.entity, }} text={EVENTS[event].text} restricted={restricted} EntityPill={EVENTS[event].EntityCell} PlayerPill={EVENTS[event].PlayerCell} />
额外注意事项
- 配置对象中存储的
Icon是已经创建完成的JSX元素(ReactNode),可以直接插值渲染,不属于组件函数调用,不会触发Hook问题。 - 哪怕当前Pill组件内部暂时没有使用Hook,也不要用直接调用函数的写法,后续迭代中一旦给Pill组件加了Hook调用,就会再次触发同类报错。
内容的提问来源于stack exchange,提问作者Eon Sberk
相关产品推荐
相关产品推荐

