search

Home  >  Q&A  >  body text

Uncaught invariant violation: Rendered with more hooks than previous render

I have a component that looks like this (very simplified version):

const component = (props: PropTypes) => {

    const [allResultsVisible, setAllResultsVisible] = useState(false);

    const renderResults = () => {
        return (
            <section>
                <p onClick={ setAllResultsVisible(!allResultsVisible) }>
                    More results v
                </p>
                {
                    allResultsVisible &&
                        <section className="entity-block--hidden-results">
                            ...
                        </section>
                }
            </section>
        );
    };

    return <div>{ renderResults() }</div>;
}

When I load a page that uses this component, I get this error: Uncaught Invariant Violation: Rendered more hooks than during the previous render. I tried to find an explanation for this error, but my search returned no results result.

When I slightly modify the component:

const component = (props: PropTypes) => {

    const [allResultsVisible, setAllResultsVisible] = useState(false);

    const handleToggle = () => {
        setAllResultsVisible(!allResultsVisible);
    }

    const renderResults = () => {
        return (
            <section>
                <p onClick={ handleToggle }>
                    More results v
                </p>
                {
                    allResultsVisible &&
                        <section className="entity-block--hidden-results">
                            ...
                        </section>
                }
            </section>
        );
    };

    return <div>{ renderResults() }</div>;
}

I no longer receive this error. Is it because I included the setState function in the jsx returned by renderResults? It would be great if there was an explanation as to why this fix works.

P粉477369269P粉477369269233 days ago551

reply all(2)I'll reply

  • P粉729198207

    P粉7291982072024-03-26 10:59:40

    I also encountered the same problem. What I'm doing is this:

    const Table = (listings) => {
    
        const {isLoading} = useSelector(state => state.tableReducer);
    
        if(isLoading){
            return <h1>Loading...</h1>
        }
    
        useEffect(() => {
           console.log("Run something")
        }, [])
    
        return (<table>{listings}</table>)
    }

    What I think is happening is that on the first render, the component returns early and useEffect doesn't run. When the isLoading state changes and useEffect runs, I get the error - The hook renders more times than it did before.

    A simple change solved the problem:

    const Table = (listings) => {
        
        const {isLoading} = useSelector(state => state.tableReducer);
            
        useEffect(() => {
            console.log("Run something")
        }, [])
        
        if(isLoading){
            return <h1>Loading...</h1>
        }
        return (<table>{listings}</table>)
    }

    reply
    0
  • P粉448346289

    P粉4483462892024-03-26 10:39:49

    The fix works because the first code example (the wrong code) calls a function inside onClick, while the second code example (the working code example) passes the function Gave onClick. The difference is those all-important parentheses, which in JavaScript mean "call this code".

    Think of it this way: In the first code example, renderResults is called every time a component is rendered. Each time this happens, setAllResultsVisible(!allResultsVisible) is called instead of waiting for the click. Since React performs rendering on its own schedule, there's no way to determine how many times this will occur.

    From React documentation:

    React processing event document

    Note: I can't get the exact error message when running the first code example in the sandbox. My error involves an infinite loop. Maybe a newer version of React produces the error described?

    reply
    0
  • Cancelreply