React手风琴组件addEventListener在首次渲染时不生效问题求助
问题根因
你将document.querySelectorAll逻辑写在了组件函数的顶层,首次渲染时DOM还未挂载到页面,此时查询得到的是空节点集合,useEffect执行时也用的是空集合绑定事件,自然点击无响应。
修改代码触发热重载时,组件重新执行顶层逻辑,此时页面已有已渲染的DOM节点,querySelector能正常拿到元素,事件绑定才生效。
修复方案1:最小改动适配现有代码
把DOM查询逻辑移到useEffect内部,同时补充卸载时的事件移除逻辑避免内存泄漏:
import { useEffect } from "react" function FAQ () { useEffect(() => { // DOM查询移到useEffect内部,保证DOM已挂载后再查询 const accordionBtn = document.querySelectorAll('.accordionTitle') const allTexts = document.querySelectorAll('.text') function toggleAccordion (el: Event) { const targetText = (el?.currentTarget as Element).nextElementSibling?.classList const target = (el?.currentTarget as Element).classList if (!(targetText?.contains('show'))) { accordionBtn.forEach(function (el) { el.classList.remove('accordionTitleActive') allTexts.forEach(function (el) { el.classList.remove('show') }) }) targetText?.add('show') target?.add('accordionTitleActive') } } accordionBtn.forEach(function (el) { el?.addEventListener('click', toggleAccordion) }) // 清理函数:组件卸载时移除事件监听 return () => { accordionBtn.forEach(function (el) { el?.removeEventListener('click', toggleAccordion) }) } }, []) return ( <div> <ul className='accordion'> <li> <h2 className='accordionTitle'>Title1 </h2> <div className='text show'> Content1 </div> </li> <li> <h2 className='accordionTitle'>Title2 </h2> <div className='text'> Content2 </div> </li> <li> <h2 className='accordionTitle'>Title3 </h2> <div className='text'> Content3 </div> </li> </ul> </div> ) } export default FAQ
修复方案2:更符合React规范的实现(推荐)
React不推荐直接操作DOM、手动绑定事件,改用状态控制类名切换即可,代码更易维护也不会出现挂载时序问题:
import { useState } from "react" // 提前定义手风琴数据,方便后续扩展 const faqList = [ { title: 'Title1', content: 'Content1' }, { title: 'Title2', content: 'Content2' }, { title: 'Title3', content: 'Content3' }, ] function FAQ () { // 用状态存储当前激活的索引,默认激活第一个 const [activeIndex, setActiveIndex] = useState(0) return ( <div> <ul className='accordion'> {faqList.map((item, index) => ( <li key={index}> <h2 className={`accordionTitle ${activeIndex === index ? 'accordionTitleActive' : ''}`} onClick={() => setActiveIndex(index)} > {item.title} </h2> <div className={`text ${activeIndex === index ? 'show' : ''}`}> {item.content} </div> </li> ))} </ul> </div> ) } export default FAQ
内容的提问来源于stack exchange,提问作者Johnny92
相关产品推荐
相关产品推荐

