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

React报错:Rendered fewer hooks than expected问题排查求助

排查React组件Hook报错:Rendered fewer hooks than expected

在项目中使用ScrollRenderingCom函数组件时,已严格遵循React Hook规则:未在循环、条件或嵌套函数内调用Hook,所有Hook均放置在return语句之前,但仍出现报错:

Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement

ScrollRenderingCom组件代码

function ScrollRenderingCom(props) {
const { children, aproximateHeight = 200} = props;
const [isIntersecting, setIsIntersecting] = useState(false);
const fakeComponent = useRef(null);


const callback = useCallback((entries) => {
    const [entry] = entries;
    if (entry.isIntersecting) {
        setIsIntersecting(true)
    }
}, []);

useEffect(() => {
    const options = {
        root: null,
        rootMargin: '0px',
        threshold: 0,
    }
    const observer = new IntersectionObserver(callback, options);
    observer.observe(fakeComponent.current);
    return () => {
        observer.disconnect();
    }
}, [callback]);

return (
    isIntersecting ? children 
                    : 
                    <div 
                        ref={fakeComponent}
                        className={children.type().props.className}
                        style={{ visibility: 'hidden',
                                 minHeight: aproximateHeight + 'px' }}
                    >
                    </div>
  )
}

报错栈追踪

at ScrollRenderingCom (http://localhost:3000/static/js/bundle.js:3468:5)
at div
at PostSection1
at Home
at Routes (http://localhost:3000/static/js/bundle.js:229775:5)
at div
at Router (http://localhost:3000/static/js/bundle.js:229708:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:228517:5)
at App

Child组件代码

function Child() {
const [successMessage, setSuccessMessage] = useState(false);

useEffect(() => {
    if (successMessage) {
        alert('this app works correctly')
    }
    return () => {setSuccessMessage(false)}
})

const handleOnClick = () => {
    setSuccessMessage(true);
}
return(
    <div className="child"
         style={{width: '500px', 
                 height: '200px',
                 backgroundColor: 'green',
                 color: 'white'
                }} 
    >
        <h1>this component should be rendered when
            the empty blue div element completely 
            enters the viewport
        </h1>
        <h3>if you see the above message click Yes</h3>
        <button onClick={handleOnClick} >Yes</button>
    </div>
   )
}

问题原因

报错的核心是代码中的children.type()调用:

  • children.type()会直接执行Child组件的函数,这意味着在ScrollRenderingCom的渲染过程中,会触发Child组件内部的Hook(useState、useEffect)调用。
  • 当isIntersecting为false时,ScrollRenderingCom不渲染children,但此时已经调用了Child的Hook;当isIntersecting变为true时,渲染children会再次调用Child的Hook,导致两次渲染周期中Hook调用数量不一致,触发React的Hook校验报错。

解决方案

不要通过调用组件函数的方式获取props,直接从children的props属性中读取即可(适用于children为单个React元素的场景):

修改ScrollRenderingCom中的className赋值:

className={children.props.className}

修复后的完整ScrollRenderingCom组件代码:

function ScrollRenderingCom(props) {
const { children, aproximateHeight = 200} = props;
const [isIntersecting, setIsIntersecting] = useState(false);
const fakeComponent = useRef(null);


const callback = useCallback((entries) => {
    const [entry] = entries;
    if (entry.isIntersecting) {
        setIsIntersecting(true)
    }
}, []);

useEffect(() => {
    const options = {
        root: null,
        rootMargin: '0px',
        threshold: 0,
    }
    const observer = new IntersectionObserver(callback, options);
    observer.observe(fakeComponent.current);
    return () => {
        observer.disconnect();
    }
}, [callback]);

return (
    isIntersecting ? children 
                    : 
                    <div 
                        ref={fakeComponent}
                        className={children.props.className}
                        style={{ visibility: 'hidden',
                                 minHeight: aproximateHeight + 'px' }}
                    >
                    </div>
  )
}

另外,Child组件中的useEffect缺少依赖数组,建议补充完整,避免不必要的重复执行:

useEffect(() => {
    if (successMessage) {
        alert('this app works correctly')
    }
    return () => {setSuccessMessage(false)}
}, [successMessage])

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 08:54:23