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粉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>) }
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?