I'm doing something wrong in my approach trying to set a className
based on the useState
from a sub navigation map.
(code stripped):
const [activeLink, setActiveLink] = useState(0)
// removed code
{items.map((item, key) => {
const { title, link } = item
return (
<React.Fragment key={key}>
<Link
to={`/${link}`}
className={activeLink === 1 ? 'active' : ''}
>
{title}
</Link>
</React.Fragment>
)
})}
Attempt 1
{items.map((item, key) => {
const { title, link } = item
let testLink = null
testLink = pathname.toString().includes(link)
if (testLink === true && activeLink === 0) setActiveLink(1)
if (testLink === false && activeLink === 1) setActiveLink(0)
return (
<React.Fragment key={key}>
<Link
to={`/${link}`}
className={activeLink === 1 ? 'active' : ''}
>
{title}
</Link>
</React.Fragment>
)
})}
Throws the error of:
Too many re-renders. React limits the number of renders to prevent an infinite loop.
Attempt 2
const handleactive = link => (pathname.toString().includes(link) ? 1 : 0)
useEffect(() => {
if (activeLink === false && handleactive() === 1) return setActiveLink(1)
return setActiveLink(0)
}, [activeLink])
Attempt 3
const handleactive = link => {
if (activeLink === 0 && pathname.toString().includes(link) === true) return setActiveLink(1)
return setActiveLink(0)
}
{items.map((item, key) => {
return (
<React.Fragment key={key}>
<Link
to={`/${link}`}
className={activeLink === 1 ? 'active' : ''}
handleactive={handleactive(link)}
>
{title}
</Link>
</React.Fragment>
)
})}
Research
- Uncaught Invariant Violation: Too many re-renders. React limits the number of renders to prevent an infinite loop
- Too many re-renders. React limits the number of renders to prevent an infinite loop. Next js error
- "Error: Too many re-renders. React limits the number of renders to prevent an infinite loop."
- React limits the number of renders to prevent an infinite loop...Too many re-renders
- Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. React js
- How to avoid Error: Too many re-renders. React limits the number of renders to prevent an infinite loop
What am I doing wrong and how can I, in a map, update the state?
CodePudding user response:
You can't call the setState
function of useState
during a render.
The map is run on each render therefore each render there is a chance (conditions dependent) that you are calling setActiveLink
.
If you wish to update state per item within a map, you probably want to create an extra component for the Links.
The links can then either keep their own state or set the parent state via callback functions passed to them from the parent.
// Map in existing parent component
{items.map((item, key) => {
return (
<NavLink
key={key}
{...item}
pathname={pathname}
/>
)
})}
// New component
const NavLink = ({title, link, pathname}) => {
const active = useMemo(() => {
return pathname.toString().includes(link)
}, [pathname, link]);
return (
<Link
to={`/${link}`}
className={active ? 'active' : ''}
>
{title}
</Link>
);
}
CodePudding user response:
You don’t have to execute and return the state method.
remove the return and execute only, that can solve the issue
CodePudding user response:
What is happening, you are calling setActiveLink
inside of your map, which triggers a render. After that render, the code inside the map gets called, calling setActiveLink
again, causing another re-render, which eventually causes an infinite loop.
You need to put your setActiveLink
into an onClick that only calls it once.
Example 3:
<Link
to={`/${link}`}
className={activeLink === 1 ? 'active' : ''}
onClick={() => handleactive(link)}
>