Reputation: 3275
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 the page this component is used on, I get this error: Uncaught Invariant Violation: Rendered more hooks than during the previous render.
I tried to find an explanation of this error, but my searching returned no results.
When I modify the component slightly:
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 get that error. Is it because I included the setState
function within the jsx that is returned by renderResults
? It would be great to have an explanation of why the fix works.
Upvotes: 173
Views: 338452
Reputation: 51
You cannot have an early return before a useEffect hook
for example
if(!userId) return
useEffect(() => {
//logic
})
if the condition by which you are returning early is required in the useEffect logic move it inside otherwise make sure that check happens after all useEffects
useEffect(() => {
if(!userId) return
//logic
})
// alternatively
useEffect(() => {
//logic
})
if(!userId) return
Upvotes: 3
Reputation: 71
If you get this error on a really big project with the particular component having so much code:
ERROR Error: Rendered more hooks than during the previous render.
It is likely due to the fact that the new hook you are trying to call, for example a useEffect hook, that was not there before, is unreachable.
Try moving the hook up a bit as high as possible in the code and this should solve the issue. If not, please refer to the docs and make sure you are properly using React hooks.
Upvotes: 5
Reputation: 29
import React from 'react'
const AddEmployee = () => {
return (
<>AddEmployee</>
)
}
export default AddEmployee
import { Link, useNavigate } from 'react-router-dom';
const ListEmployeeComponent = () => {
const navigator1 = useNavigate();
//below all the normal code like useEffect....
// and
function addNewEmp()
{
navigator1('/AddEmployee') //defined a path in routes
}
return (
<button onClick={addNewEmp}>Add Employee</button>
JSX)
Note : if you add the const navigator1 = useNavigate(); below useEffect and all you will get an error Uncaught Error: Rendered more hooks than during the previous render. and your page will not render for me this worked please try this and let me know
Upvotes: 0
Reputation: 201
One possible reason for this error is that use
Component() // error
<Component /> // good
here is the explanation:
If you call the component function directly, it's just a function that returns JSX (no state, no effects). The hook inside does not work properly, it may be considered a hook of a parent component. If you use <Component />
, it is equivalent to executing React.createElement, and the hook inside will work as expected.
Upvotes: 20
Reputation: 11
I think this could be classified as a bug, but curious to get the developers' thoughts on this. Basically, the Rendered more hooks than during the previous render error does not seem to get thrown when the transition is from 0 hooks to a positive number of hooks.
The error does get thrown when, for example, a component goes from rendering 1 hook to 2 hooks.
React version: 17.0.2
Upvotes: 0
Reputation: 20158
In my case I have used setState() hook inside if condition in the following way, so I got an error after that I have resolved. According to react hook document we should not use hooks inside if condition.
Error:
import React, { useState, useCallback } from 'react';
import './style.css';
export default function App() {
const [count, setCount] = useState(0);
if(count < 10){
return (
<div>
<h1>Hello Count!</h1>
<button onClick={useCallback(setCount((count) => count + 1))}>
click to add
</button>
</div>
);
} else {
return <div>Count reached 10!</div>
}
}
Solution:
import React, { useState, useCallback } from 'react';
import './style.css';
export default function App() {
const [count, setCount] = useState(0);
const handleIncrement = useCallback(() => {
setCount((count) => count + 1)
})
if(count < 10){
return (
<div>
<h1>Hello Count!</h1>
<button onClick={handleIncrement}>click to add</button>
</div>
);
} else {
return <div>Count reached 10!</div>
}
}
Upvotes: 2
Reputation: 2043
I faced the same issue. What I was doing was something like 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>)
}
I think what was happening was that on the first render, the component returned early and the useEffect didn't run. When the isLoading state changed, the useEffect ran and I got the error - the hook rendered more times than the previous render.
A simple change fixed it:
const Table = (listings) => {
const {isLoading} = useSelector(state => state.tableReducer);
useEffect(() => {
console.log("Run something")
}, [])
if(isLoading){
return <h1>Loading...</h1>
}
return (<table>{listings}</table>)
}
Upvotes: 157
Reputation: 197
See the question can be React :
In both the cases thing can be like you have a conditional statement calling the same function which returns render from different places like both wrapped in a parent return function:
const parentFunc = () => {
if(case==1)
return function_a();
if (case==2)
return function_b();
}
now function_a() could be a function creating two or one hook suppose useStyle() or anything else
and function_b() could be a function creating no hook.
Now, when parentFunc returns function_a() rendering one hook and function_b() rendering no hook then react will tell you that from the same render function two different renders were returned one with two or one hook and the other with one hook this disparity leads to the error. Error being
less hooks were rendered. And the error is quite obvious.
When cases are reversed and function_b() is returned first cause of the conditionals then react will tell you that from the same render function different renders were returned and error will be .
Rendered more hooks than previous render.
Now, Solution:
Change the code flow like maybe create function_ab() which will ensure all the hooks being used are rendered and in that function:
const function_ab = () => {
if(case==1)
return (<div></div>) //or whatever
if(case==2)
return (<div>I am 2 </div>) //or whatever
}
Upvotes: 11
Reputation: 2614
The issue is within the onClick
as the setAllResultsVisible
is called, it will trigger state change and result on every render
onClick={ setAllResultsVisible(!allResultsVisible) }
Change this to function call instead:
onClick={_ => setAllResultsVisible(!allResultsVisible) }
Upvotes: -2
Reputation: 16025
Even after the fixes above, there are a few other causes as well for this error. I am writing below one use case which occurred for me.
function Comp(props){return <div>{props.val}</div>}
This component can be called in the following ways in jsx:
1. <Comp val={3} /> // works well
2. { Comp({val:3}) } // throws uncaught invariant violation error, at least it throw in my case, may be there were other factors, but changing back to first way removed that problem
Upvotes: 13
Reputation: 1773
You can simply change your onlick event add () =>
before setAllResultsVisible
<p onClick={() => setAllResultsVisible(!allResultsVisible) }>
More results v
</p>
and it will work perfectly
Upvotes: 24
Reputation: 5852
The fix works because the first code sample (the erroring one) invokes a function inside onClick
, while the second (the working one) passes a function to onClick
. The difference is those all-important parentheses, which in JavaScript mean 'invoke this code'.
Think of it this way: in the first code sample, every time component
is rendered, renderResults
is invoked. Every time that happens, setAllResultsVisible(!allResultsVisible)
, rather than waiting for a click, is called. Since React performs the render on its own schedule, there's no telling how many times that will happen.
From the React docs:
With JSX you pass a function as the event handler, rather than a string.
Note: I wasn't able to get this exact error message when running the first code sample in a sandbox. My error referred to an infinite loop. Maybe a more recent version of React produces the error described?
Upvotes: 64