React+Tailwind实现导航点击/hover切换元素时无限重渲染问题求助
问题描述
我用React和Tailwind开发功能,需求是:导航按钮支持点击固定切换、悬停临时切换显示三个元素中的一个;初始默认显示Overview,悬停导航链接时临时展示对应元素,鼠标离开后恢复为选中的固定元素。但给导航链接加onClick监听后,出现无限重渲染错误:
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
相关代码:
const [displayOverview, setDisplayOverview] = useState(false); const [displayProjects, setDisplayProjects] = useState(false); const [displayDesigns, setDisplayDesigns] = useState(false); const [selectedNavElement, setSelectedNavElement] = useState('overview'); const [displayFixed, setDisplayFixed] = useState(true) <ul className='flex lg:text-left gap-24'> <li onMouseEnter={() => {setDisplayOverview(true); setDisplayFixed(false)}} onMouseLeave={() => {setDisplayOverview(false); setDisplayFixed(true)}} > Overview </li> <li onMouseEnter={() => {setDisplayDesigns(true); setDisplayFixed(false)}} onMouseLeave={() => {setDisplayDesigns(false); setDisplayFixed(true)}} > Designs </li> <li onMouseEnter={() => {setDisplayProjects(true); setDisplayFixed(false)}} onMouseLeave={() => {setDisplayProjects(false); setDisplayFixed(true)}} > <span onClick={setSelectedNavElement('projects')}>Projects</span> </li> </ul> </nav> {/* Content */} <section className='bg-red-500'> {displayOverview && <Overview />} {displayDesigns && <Designs />} {displayProjects && <Projects />} {(selectedNavElement === 'overview' && displayFixed === true) && <Overview />} {(selectedNavElement === 'designs' && displayFixed === true) && <Designs />} {(selectedNavElement === 'projects' && displayFixed === true) && <Projects />} </section>
解决方案
1. 先修复无限重渲染问题
无限重渲染的直接原因是onClick绑定的不是函数,而是直接执行了setSelectedNavElement('projects')——每次组件渲染都会调用这个状态更新函数,触发新一轮渲染,形成循环。
把点击事件改成函数形式,其他导航项同理:
<span onClick={() => setSelectedNavElement('projects')}>Projects</span>
2. 优化状态管理(简化逻辑)
当前状态变量过多(三个displayXxx),可以合并成一个hoveredElement状态,用来记录当前悬停的元素标识,逻辑会更简洁:
- 用
hoveredElement存储当前悬停的元素('overview'/'designs'/'projects',无悬停时为null) - 保留
selectedNavElement存储点击选中的固定元素 - 渲染时优先判断是否有悬停元素,有则显示对应组件;无则显示选中的固定组件
3. 完整实现代码
import { useState } from 'react'; import Overview from './Overview'; import Designs from './Designs'; import Projects from './Projects'; function NavContent() { const [selectedNavElement, setSelectedNavElement] = useState('overview'); const [hoveredElement, setHoveredElement] = useState(null); // 确定当前要显示的元素:悬停优先级高于选中 const currentDisplay = hoveredElement || selectedNavElement; return ( <> <nav> <ul className='flex lg:text-left gap-24'> <li onMouseEnter={() => setHoveredElement('overview')} onMouseLeave={() => setHoveredElement(null)} onClick={() => setSelectedNavElement('overview')} > Overview </li> <li onMouseEnter={() => setHoveredElement('designs')} onMouseLeave={() => setHoveredElement(null)} onClick={() => setSelectedNavElement('designs')} > Designs </li> <li onMouseEnter={() => setHoveredElement('projects')} onMouseLeave={() => setHoveredElement(null)} onClick={() => setSelectedNavElement('projects')} > Projects </li> </ul> </nav> <section className='bg-red-500'> {currentDisplay === 'overview' && <Overview />} {currentDisplay === 'designs' && <Designs />} {currentDisplay === 'projects' && <Projects />} </section> </> ); } export default NavContent;
逻辑说明
- 悬停行为:鼠标进入导航项时设置
hoveredElement,离开时清空,此时优先显示悬停对应的组件 - 点击行为:点击导航项时更新
selectedNavElement,鼠标离开后hoveredElement清空,自动切换到显示选中的固定组件 - 初始状态:默认选中'overview',无悬停时直接显示Overview组件
内容的提问来源于stack exchange,提问作者user17511401
相关产品推荐
相关产品推荐

