React自定义usePoll Hook触发setState时hook渲染数量少于预期报错求助
问题根因
你触发Rendered fewer hooks than expected报错的核心原因是违反了React Hook的调用规则:所有Hook必须在组件的最顶层作用域调用,不能包裹在条件判断、循环、嵌套函数内部。
你当前的代码里把usePoll放在了if ([...Statues].includes(report.status))的条件块内,当这个条件第一次为true时会调用usePoll,后续渲染如果条件变为false就不会调用这个Hook,导致两次渲染调用的Hook数量不一致,就触发了这个报错。
修复方案
第一步:改造usePoll自定义Hook,增加启停控制参数
给usePoll加一个enabled参数,用来控制轮询是否真正执行,不需要在外层通过条件判断是否调用Hook:
export function usePoll(callback: IntervalFunction, delay: number, enabled: boolean = true) { const savedCallback = useRef<IntervalFunction | null>() useEffect(() => { savedCallback.current = callback }, [callback]) useEffect(() => { // 未启用时直接返回,不创建定时器 if (!enabled) return function tick() { if (savedCallback.current !== null) { savedCallback.current() } } const id = setInterval(tick, delay) return () => clearInterval(id) }, [delay, enabled]) // 把enabled加入依赖项 }
第二步:调整组件内的调用逻辑,把Hook移到顶层
把原来的条件判断改为传给usePoll的enabled参数,所有Hook都放在组件最顶层调用:
const BankLink: React.FC = ({ report: _report }) => { const [report, setReport] = React.useState(_report) // 先计算轮询启用条件 const pollEnabled = [...Statues].includes(report.status) // 顶层调用usePoll,不使用条件包裹 usePoll(async () => { const initialStatus = _report.status const { result } = await apiPost(`/links/search`) const currentReport = result.results.filter((item: { id: string; }) => item.id === _report.id) if (currentReport[0]?.status !== initialStatus) { // 直接调用setReport更新状态即可,如果你封装了handleSetReportState逻辑也可以正常调用 setReport(currentReport[0]) console.log('status changed') } else { console.log('status unchanged') } }, 5000, pollEnabled) // ... 剩余组件逻辑 }
额外注意点
- 你原代码里
apiPost调用多了一个多余的右大括号,记得删掉避免语法报错 - 取
currentReport[0]的时候最好加可选链?.判断,避免接口返回空数组时报错 - 如果
_report属性可能会被外层组件更新,建议加useEffect同步props到state:
useEffect(() => { setReport(_report) }, [_report])
内容的提问来源于stack exchange,提问作者unicorn_surprise
相关产品推荐
相关产品推荐

