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>
)
})}
{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.
const handleactive = link => (pathname.toString().includes(link) ? 1 : 0)
useEffect(() => {
if (activeLink === false && handleactive() === 1) return setActiveLink(1)
return setActiveLink(0)
}, [activeLink])
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>
)
})}
What am I doing wrong and how can I, in a map, update the state?
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>
);
}